在 Perl 中如何循环遍历一个类的所有方法?有没有关于 Perl 内省或反射的好的在线参考资料?
6 回答
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 文档。
您还会注意到我使用了 Class::MOP 而不是 Moose。Class::MOP (MOP = Meta-Object Protocol) 是 Moose 构建的基础。如果您正在使用非 Moose 类,使用 Moose 进行内省不会给您带来任何好处。
如果你愿意,你可以use Moose ()
代替Moose::Meta::Class->initialize
CMOP。
您可以使用已经提供的答案轻松获取类的已定义方法的列表。但是,Perl 是一种动态语言,这意味着以后可能会定义更多的方法。确实没有办法获得任何特定类将处理的所有方法的列表。有关此类内容的更多详细信息,我在Mastering Perl中有几章。
人们在没有告诉你限制的情况下给你(和支持)答案。
Adam 提到了他的Class::Inspector,但它并没有真正起作用,因为它试图做一些动态语言不做的事情(而且那是静态的 :) 例如,这是一个片段,其中 Class::Inspector 不返回任何方法,但我仍然可以调用该VERSION
方法(以及isa
and can
):
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
(并且仍然缺少VERSION
、isa
和can
):
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() 仍然返回 false,而不是死亡,在这些情况下:
if( eval { $object->can( 'method_name' ) } )
{
$object->( @args );
}
在一般情况下,您必须检查符号表(除非您使用 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手册页。
你可能想要 Class::Inspector->methods('Your::Class')。
纳夫说。
取决于您的意思是任何课程,或者您是否正在实施自己的课程。对于后者,我使用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
当我忘记它时,我会把它留在这里。这是非常强大的;太糟糕了,它是如此的偏僻,以至于大多数 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::;