【问题标题】:How do I loop over all the methods of a class in Perl?如何循环遍历 Perl 中类的所有方法?
【发布时间】:2009-06-20 14:23:12
【问题描述】:

如何在 Perl 中遍历一个类的所有方法?有没有关于 Perl 内省或反射的好的在线参考资料?

【问题讨论】:

标签: perl reflection introspection


【解决方案1】:

Todd Gardner 给出的使用 Moose 的建议是一个很好的建议,但他选择的示例代码并不是很有帮助。

如果您使用类检查非 Moose,您可以执行以下操作:

use Some::Class;
use Class::MOP;

my $meta = Class::MOP::Class->initialize('Some::Class');

for my $meth ( $meta->get_all_methods ) {
    print $meth->fully_qualified_name, "\n";
}

请参阅Class::MOP::Class docs 了解有关如何进行自省的更多详细信息。

您还会注意到我使用了 Class::MOP 而不是 Moose。 Class::MOP (MOP = Meta-Object Protocol) 是 Moose 构建的基础。如果您使用的是非 Moose 类,则使用 Moose 进行内省不会给您带来任何好处。

如果你愿意,你可以用use Moose ()Moose::Meta::Class->initialize 代替 CMOP。

【讨论】:

  • +1:太好了,我不知道你可以这样使用 Class::MOP。
  • 我在回答中解释了这一点的局限性。即使是一个空类,它也无法从 UNIVERSAL 中获取方法,并且无法从 AUTOLOAD 中获取任何方法。
  • @brian d foy:使用 AUTOLOAD 很糟糕,嗯?
【解决方案2】:

您可以使用已经提供的答案轻松获取类的已定义方法的列表。但是,Perl 是一种动态语言,这意味着以后可能会定义更多的方法。确实没有办法获得任何特定类将处理的所有方法的列表。有关此类内容的更多详细信息,我在Mastering Perl 中有几章。

人们在没有告诉你限制的情况下给你(和支持)答案。

Adam 提到了他的 Class::Inspector,但它并没有真正起作用,因为它试图做一些动态语言不做的事情(而且那是静态的 :) 例如,这是一个 sn-p,其中 Class::Inspector不返回任何方法,但我仍然可以调用VERSION 方法(以及isacan):

    BEGIN {

package Foo;

our $VERSION = '1.23'
}

use Class::Inspector;

my $methods = Class::Inspector->methods( 'Foo' );

print "Methods are [@$methods]\n"; # reports nothing

print Foo->VERSION, "\n";

这是另一种情况,我可以调用任何我喜欢的方法,但 Class::Inspector 只返回 AUTOLOAD(并且仍然缺少 VERSIONisacan):

BEGIN {

package Foo;

our $VERSION = '1.23';

my $object = bless {}, __PACKAGE__;

sub AUTOLOAD { $object }

}

use Class::Inspector;

my $methods = Class::Inspector->methods( 'Foo' );

print "Methods are [@$methods]\n"; # reports only "AUTOLOAD"

print Foo->dog->cat->bird, "\n";

奇怪的是,每个人似乎都忽略了 UNIVERSAL,可能是因为他们没有明确处理它,因为它实际上只在 @ISA 中。我可以为每个类添加一个debug 方法,而 Class::Inspector 仍然会错过它,即使它是一个已定义的方法:

BEGIN {

sub UNIVERSAL::debug { "Hello debugger!\n" }    
package Foo;
}

use Class::Inspector;

my $methods = Class::Inspector->methods( 'Foo' );

print "Methods are [@$methods]\n"; # still reports nothing

print Foo->debug, "\n";

Class::MOP 有同样的限制。

不是每个模块都会使用 AUTOLOAD,但它也不是一个晦涩或罕见的功能。如果你不介意你会错过一些方法,那么 Class::Inspector 或 Class::MOP 可能没问题。它只是不会为您提供在每种情况下您可以在类或对象上调用的每种方法的列表。

如果您有一个类或一个对象,并且想知道是否可以调用特定方法,请使用 can()。将它包装在一个 eval 块中,这样 can 可以在那些甚至不是对象的东西上调用 can() 仍然返回 false,而不是死亡,在这些情况下:

if( eval { $object->can( 'method_name' ) } )
    {
    $object->( @args );
    }

【讨论】:

    【解决方案3】:

    在一般情况下,您必须检查符号表(除非您使用 Moose)。例如,列出IO::File 包中定义的方法:

    use IO::File;
    no strict 'refs';
    print join ', ', grep { defined &{"IO::File::$_"} } keys %{IO::File::};
    

    哈希%{IO::File::}IO::File package的符号表,grep过滤掉非子例程条目(例如包变量)。

    要将其扩展为包含继承的方法,您必须递归搜索父类的符号表 (@IO::File::ISA)。

    这是一个完整的例子:

    sub list_methods_for_class {
        my $class = shift;
        eval "require $class";
        no strict 'refs';
        my @methods = grep { defined &{$class . "::$_"} } keys %{$class . "::"};
        push @methods, list_methods_for_class($_) foreach @{$class . "::ISA"};
        return @methods;
    }
    

    有关包和符号表的更多信息,请参阅perlmod 手册页。

    【讨论】:

    • 并非每个方法都必须在符号表中定义,而且您缺少 UNIVERSAL。
    • 我想知道,如果这个问题的任何解决方案,特别是这里的这个,每次调用代码时生成一个新的对象实例,如果它被回收或者每个实例都留在内存中直到程序终止.因为在 .NET 和 Java 等其他语言中使用反射/自省,必须小心在哪里实例化类对象引用,否则每次调用都可以获得新实例。
    【解决方案4】:

    取决于你的意思是,任何类,或者你是否正在实现自己的。对于后者,我使用Moose,它为这些特性提供了非常简洁的语法。来自食谱:

    my %attributes = %{ $self->meta->get_attribute_map };
    for my $name ( sort keys %attributes ) {
       my $attribute = $attributes{$name};
    
       if (   $attribute->does('MyApp::Meta::Attribute::Trait::Labeled')
       # ... keeps on
    

    【讨论】:

    • 请注意,Moose 不会找到任何属性,除非您正在自省的类是首先使用 Moose 定义的。但是,方法自省以及大多数其他位都可以正常工作。
    【解决方案5】:

    你可能想要 Class::Inspector->methods('Your::Class')。

    努夫说。

    【讨论】:

      【解决方案6】:

      我会把它留在这里,等我忘记了。这是非常强大的;太糟糕了,它是如此的偏僻,以至于大多数 Perl 程序员永远无法体验它。

      package Foo;
      use strict;
      sub foo1 {};
      sub foo2 {};
      our $foo3 = sub{};
      my $foo4 = "hello, world!";
      
      package Bar;
      use strict;
      
      # woo, we're javascript!
      (sub {
          *Bar::foo1 = sub { print "hi!"; };
          *Bar::foo2 = sub { print "hello!"; };
          $Bar::foo1 = 200;
      })->();
      
      package main;
      use strict;
      use Data::Dumper;      
      $Data::Dumper::Deparse = 1;
      
      print Dumper \%Data::Dumper::;
      print Dumper \%Foo::;
      print Dumper \%Bar::;
      

      【讨论】:

        猜你喜欢
        • 2020-09-02
        • 2017-03-09
        • 1970-01-01
        • 1970-01-01
        • 2021-03-20
        • 1970-01-01
        • 2011-12-23
        • 1970-01-01
        • 2016-08-20
        相关资源
        最近更新 更多