2010-06-04 19 views
5

¿Puede alguien decirme por qué el principal no encuentra los métodos generados por Class :: Accessor en este pequeño y trivial ejemplo?Perl Class :: Falla del accesorio, ejemplo trivial: ¿por qué?

Estas pocas líneas de código fallan con

perl codesnippets/accessor.pl 
Can't locate object method "color" via package "Critter" at 
codesnippets/accessor.pl line 6. 

ver el código:

#!/opt/local/bin/perl 
# The whole Class::Accessor thing does not work !! 

my $a = Critter->new; 
$a->color("blue"); 
$a->display; 
exit 0; 

package Critter; 
    use base qw(Class::Accessor); 
    Critter->mk_accessors ("color"); 

    sub display { 
     my $self = shift; 
     print "i am a $self->color " . ref($self) . ", whatever this word means\n"; 
    } 
+0

simplemente he intentado y vi el mismo comportamiento con la clase :: :: Accessor con clase y ratón. –

+0

No utilice Mouse o C: A, use 'Moose' –

+0

Vea [mi solución] (http://stackoverflow.com/questions/2973549/perl-classaccessor-failure-trivial-example-why/2975492#2975492) para un ejemplo de esto en 'Moose'. –

Respuesta

3

FM te está dando un buen consejo. mk_accessors necesita ejecutarse antes que el otro código. Además, normalmente pondría Critter en un archivo separado y use Critter para cargar el módulo.

Esto funciona porque use tiene efectos de tiempo de compilación. Hacer use Critter; es lo mismo que hacer BEGIN { require Critter; Critter->import; } Esto garantiza que el código de inicialización de su módulo se ejecutará incluso antes de que el resto del código se compile.

Es aceptable poner varios paquetes en un solo archivo. A menudo, crearé prototipos de objetos relacionados en un solo archivo, ya que lo tengo todo a mano mientras estoy creando prototipos. También es bastante fácil dividir el archivo en bits separados cuando llegue el momento.

Debido a esto, creo que la mejor manera de mantener varios paquetes en un archivo y trabajar con ellos como si los estuviera usando es colocar las definiciones de paquetes en BEGIN bloques que terminan en un valor verdadero. Usando mi enfoque, su ejemplo se escribiría:

#!/opt/local/bin/perl 

my $a = Critter->new; 
$a->color("blue"); 
$a->display; 

BEGIN { 
    package Critter; 
    use base qw(Class::Accessor); 

    use strict; 
    use warnings; 

    Critter->mk_accessors ("color"); 

    sub display { 
     my $self = shift; 

     # Your print was incorrect - one way: 
     printf "i am a %s %s whatever this word means\n", $self->color, ref $self; 

     # another: 
     print "i am a ", $self->color, ref $self, "whatever this word means\n"; 

    } 

    1; 
} 
+0

También he pensado en la COMIENZA el bloqueo justo más tarde en el día en que publiqué esta pregunta, y lo intentaré ahora. No quería poner archivos de paquete de clase como archivos separados porque estoy escribiendo una utilidad perl, y quiero distribuirlo como una secuencia de comandos de perl, sin tener que decir a los usuarios que instalen todos los archivos de paquetes de clase por separado y establecer su perl5lib variable –

+0

También me preguntaba por qué el Critter-> new() funcionaba y no el $ a-> color ("blue"), pero su explicación del funcionamiento interno "use" me decía por qué las funciones (por ejemplo, new()) heredado de la clase base C: A estaban disponibles. –

8

Su código está fuera de servicio. Si desea que el accesorio color esté disponible, debe invocar mk_accessorsantes de para crear su objeto y comenzar a hacer cosas con él. Por ejemplo:

package Critter; 
use base qw(Class::Accessor); 
Critter->mk_accessors("color"); 

sub display { 
    my $self = shift; 
    print $self->color, ' ', ref($self), "\n"; 
} 

package main; 
my $c = Critter->new; 
$c->color("blue"); 
$c->display; 

Más comúnmente, el código Critter estaría en su propio módulo (Critter.pm), y toda la magia mk_accessor ocurriría cuando el script principal corre use Critter - mucho antes de su script comienza a trabajar con Critter y Varmint objetos.

+1

Alternativamente, uno podría ajustar la declaración del paquete en un bloque BEGIN para asegurarse de que se ejecuta antes de intentar crear una instancia del objeto. – Ether

+0

Sí, el código está fuera de servicio, fue a propósito, como tener la parte principal al principio para verlo fácilmente, y enterrar la implementación y todo lo de la clase en la parte inferior del archivo. No está en un archivo separado porque estoy escribiendo una utilidad perl, y quiero distribuirlo como una secuencia de comandos de perl, sin tener que indicar a los usuarios que instalen todos los archivos de paquetes de clase por separado y establecer su variable perl5lib –

2

Sólo quería ofrecerle una mejor solución - no dude en esta downvote al olvido si la solución no es bienvenido, pero C :: A es realmente una mala idea este día y edad, utilice Moose:

package Critter; 
use Moose; 

has 'color' => (isa => 'Str', is => 'rw'); # Notice, this is typed 

sub display { 
    my $self = shift; 
    printf (
     "i am a %s %s whatever this word means\n" 
     , $self->color 
     , $self->meta->name 
    ); 
} 

package main; 
use strict; 
use warnings; 

my $c = Critter->new; # or my $c = Critter->new({ color => blue }); 
$c->color("blue"); 
$c->display; 
+0

O Mouse, si le importa el tiempo de inicio. – MkV

+0

Pensé que Moose no estaría disponible como RPM en repositorios predeterminados de distribuciones de Linux más conservadoras, como centos, e hizo una prueba rápida con C: A. Pero ahora veo que Moose está allí, así que podría usarlo. Como mi propósito es hacer un script perl fácilmente distribuible, trato de evitar todos los módulos modernos y/o extravagantes, para que el script pueda ejecutarse en cualquier lugar sin tener que obtener módulos de cpan directamente –

+0

'use Moose', y' Module :: Install' - que puede agregar los paquetes externos en el módulo. Incluso, sin 'Module :: Install',' Moose' probablemente esté en el sistema de cualquier usuario decente de Perl. También hará que la programación con Perl sea mucho más agradable. –

Cuestiones relacionadas