Perl Class :: Ошибка доступа, тривиальный пример - почему? - PullRequest
5 голосов
/ 04 июня 2010

Может кто-нибудь сказать мне, почему main не находит методы, сгенерированные Class :: Accessor в этом очень маленьком и тривиальном примере?

Эти несколько строк кода терпят неудачу с

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

см. Код:

#!/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";
    }

Ответы [ 3 ]

8 голосов
/ 04 июня 2010

Ваш код не в порядке. Если вы хотите, чтобы аксессор color был доступен, вам нужно вызвать mk_accessors до того, как создаст свой объект и начнет с ним что-то делать. Например:

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;

Чаще всего код Critter будет находиться в своем собственном модуле (Critter.pm), и вся магия mk_accessor произойдет, когда ваш основной скрипт выполнит use Critter - задолго до того, как ваш скрипт начнет работать с Critter и Varmint объекты.

3 голосов
/ 04 июня 2010

FM дает вам хороший совет. mk_accessors необходимо запустить перед другим кодом. Кроме того, обычно вы помещаете Critter в отдельный файл и use Critter для загрузки модуля.

Это работает, потому что use имеет эффекты времени компиляции. Выполнение use Critter; аналогично выполнению BEGIN { require Critter; Critter->import; } Это гарантирует, что код инициализации вашего модуля будет запущен до того, как остальная часть кода даже скомпилируется.

Допустимо помещать несколько пакетов в один файл. Часто я создаю прототипы связанных объектов в одном файле, так как он сохраняет все под рукой, пока я создаю прототип. Также довольно легко разбить файл на отдельные части, когда придет время.

По этой причине я считаю, что лучший способ хранить несколько пакетов в одном файле и работать с ними, как если бы я их использовал, - это поместить определения пакетов в BEGIN блоки, заканчивающиеся истинным значением. Используя мой подход, ваш пример будет написан:

#!/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;
}
2 голосов
/ 04 июня 2010

Я просто хотел предложить вам лучшее решение - не стесняйтесь опускать это до забвения, если решение не приветствуется, но C :: A действительно плохая идея в наше время, используйте 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;
...