Как я могу переопределить методы класса Perl?
Вопрос "Как я могу обезвредить метод экземпляра в Perl?" подумал. Могу ли я динамически переопределять методы Perl? Скажем, у меня есть класс вроде этого:
package MyClass;
sub new {
my $class = shift;
my $val = shift;
my $self = { val=> $val};
bless($self, $class);
return $self;
};
sub get_val {
my $self = shift;
return $self->{val}+10;
}
1;
И пусть говорят, что добавление двух чисел действительно дорого.
Я хотел бы изменить класс, чтобы $val + 10 вычислялся только при первом вызове метода на этом объекте. Последующие вызовы метода возвращают кешированное значение.
Я мог бы легко изменить способ включения кэширования, но:
- У меня есть куча таких методов.
- Я бы не стал загрязнять этот метод.
Я действительно хочу указать, что список методов, которые я знаю, всегда возвращает одно и то же значение для данного экземпляра. Затем я хочу взять этот список и передать его функции, чтобы добавить поддержку кеширования в эти методы.
Есть ли эффективный способ сделать это?
Последующие действия. Код ниже работает, но поскольку использование strict не позволяет ссылки по строке, я не на 100%, где я хочу быть.
sub myfn {
printf("computing\n");
return 10;
}
sub cache_fn {
my $fnref = shift;
my $orig = $fnref;
my $cacheval;
return sub {
if (defined($cacheval)) { return $cacheval; }
$cacheval = &$orig();
return $cacheval;
}
}
*{myfn} = cache_fn(\&myfn);
Как мне изменить только для этого?:
cache_fn(&myfn);
Ответы
Ответ 1
Вы можете перезаписать такие методы, как get_val из другого пакета, например:
*{MyClass::get_val} = sub { return $some_cached_value };
Если у вас есть список имен методов, вы можете сделать что-то вроде этого:
my @methods = qw/ foo bar get_val /;
foreach my $meth ( @methods ) {
my $method_name = 'MyClass::' . $meth;
no strict 'refs';
*{$method_name} = sub { return $some_cached_value };
}
Это то, что вы себе представляете?
Ответ 2
Я пишу о нескольких разных вещах, которые вы, возможно, захотите сделать в главе "Динамические подпрограммы" Освоение Perl. В зависимости от того, что вы делаете, вы можете обернуть подпрограмму или переопределить ее, или подкласс, или всевозможные другие вещи.
Perl - динамический язык, поэтому вы можете сделать много черной магии. Его разумным способом является трюк.
Ответ 3
Я никогда не пробовал это с помощью методов, но Memoize может быть тем, что вы ищете. Но обязательно прочитайте caveats.
Ответ 4
Не полезно в вашем случае, но если ваш класс был написан в Moose, тогда вы можете динамически переопределять методы, используя Класс:: MOP.
{
package MyClass;
use Moose;
has 'val' => ( is => 'rw' );
sub get_val {
my $self = shift;
return $self->val + 10;
}
}
my $A = MyClass->new( val => 100 );
say 'A: before: ', $A->get_val;
$A->meta->remove_method( 'get_val' );
$A->meta->add_method( 'get_val', sub { $_[0]->val } );
say 'A: after: ', $A->get_val;
my $B = MyClass->new( val => 100 );
say 'B: after: ', $B->get_val;
# gives u...
# => A: before: 110
# => A: after: 100
# => B: after: 100
Ответ 5
Как мне изменить только для этого?:
cache_fn (\ & myfn);
Хорошо, основываясь на вашем текущем примере, вы можете сделать что-то вроде этого....
sub cache_fn2 {
my $fn_name = shift;
no strict 'refs';
no warnings 'redefine';
my $cache_value = &{ $fn_name };
*{ $fn_name } = sub { $cache_value };
}
cache_fn2( 'myfn' );
Однако, глядя на этот пример, я не могу не думать, что вместо этого можно использовать Memoize?