如何重新定义Perl类方法?

时间:2009-03-11 16:04:00

标签: perl

问题"How can I monkey-patch an instance method in 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仅在我第一次调用该对象上的方法时计算。对该方法的后续调用将返回缓存值。

我可以轻松修改方法以包含缓存,但是:

  • 我有很多像这样的方法。
  • 我宁愿不弄脏这种方法。

我真正想要做的是指定一个方法列表,我知道它总是返回给定实例的相同值。然后我想获取此列表并将其传递给函数以向这些方法添加缓存支持

有没有有效的方法来实现这一目标?

跟进。下面的代码有效,但因为use 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);

5 个答案:

答案 0 :(得分:14)

您可以从另一个包中覆盖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 };
}

这就是你想象的吗?

答案 1 :(得分:5)

我在Mastering Perl的“动态子程序”一章中写了几篇你可能想要做的事情。根据您正在做的事情,您可能希望包装子例程,或重新定义它,子类或其他各种事物。

Perl是一种动态语言,所以你可以做很多黑魔法。明智地使用它是诀窍。

答案 2 :(得分:3)

我从来没有尝试过方法,但Memoize可能就是你要找的东西。但请务必阅读caveats

答案 3 :(得分:2)

在你的情况下没有用,但你的课程是用Moose编写的,那么你可以使用Class::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

答案 4 :(得分:2)

  
    

如何修改才能执行此操作?:

         

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吗?