如何在 Perl 中循环访问类的所有方法?

发布于 2024-07-25 05:57:54 字数 56 浏览 2 评论 0原文

如何在 Perl 中循环访问一个类的所有方法? 有没有关于 Perl 内省或反射的好的在线参考?

How do you loop over all the methods of a class in Perl? Are there any good online references to Perl introspection or reflection?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

听风吹 2024-08-01 05:57:54

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 = 元对象协议)是 Moose 构建的基础。 如果您正在使用非 Moose 类,那么使用 Moose 进行内省不会给您带来任何好处。

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

The recommendation Todd Gardner gave to use Moose is a good one, but the example code he chose isn't very helpful.

If you're inspecting a non-Moose using class, you'd do something like this:

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";
}

See the Class::MOP::Class docs for more details on how to do introspection.

You'll also note that I used Class::MOP instead of Moose. Class::MOP (MOP = Meta-Object Protocol) is the base on which Moose builds. If you're working with non-Moose classes, using Moose to introspect doesn't gain you anything.

If you wanted, you could use Moose () and Moose::Meta::Class->initialize instead of CMOP.

So要识趣 2024-08-01 05:57:54

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

人们在不告诉您限制的情况下给您(并投票)答案。

Adam 提到了他的 Class::Inspector,但它并没有真正起作用,因为它试图做一些动态语言不会做的事情(而且是静态的:)例如,这里有一个片段,其中 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() ,在这些情况下仍然返回 false,而不是死亡:

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

You can easily get a list of the defined methods of a class using the answers already provided. However, Perl is a dynamic language, which means more methods may be defined later. There really isn't a way to get a list of all of the methods to which any particular class will handle. For a lot more detail on this sort of stuff, I have a few chapters in Mastering Perl.

People are giving you (and upvoting) answers without telling you about the limitations.

Adam mentions his Class::Inspector, but it doesn't really work because it's trying to do something a dynamic language doesn't do (and that's be static :) For instance, here's a snippet where Class::Inspector returns no methods, but I can still call the VERSION method (as well as 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";

Here's another case where I can call any method I like, but Class::Inspector only returns AUTOLOAD (and still missing VERSION, isa, and 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";

Curiously, everyone seems to ignore UNIVERSAL, probably because they don't explicitly handle it since it's only virtually in @ISA. I can add a debug method to every class, and Class::Inspector still misses it even though it's a defined method:

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 has the same limitations.

Not every module is going to use AUTOLOAD, but it's not an obscure or rare feature either. If you don't mind that you are going to miss some of the methods then Class::Inspector or Class::MOP might be okay. It's just not going to give you a list of every method you can call on a class or an object in every case.

If you have a class or an object and you want to know if you can call a particular method, use can(). Wrap it in an eval block so can can call can() on things that aren't even objects to still get back false, instead of death, in those cases:

if( eval { $object->can( 'method_name' ) } )
    {
    $object->( @args );
    }
枫林﹌晚霞¤ 2024-08-01 05:57:54

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

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

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

要扩展它以包含继承的方法,您必须递归搜索父类的符号表 (@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 手册页。

In the general case, you'll have to inspect the symbol table (unless you use Moose). For example, to list the methods defined in the IO::File package:

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

The hash %{IO::File::} is the symbol table of the IO::File package, and the grep filters out non-subroutine entries (e.g. package variables).

To extend this to include inherited methods, you have to recursively search the symbol tables of the parent classes (@IO::File::ISA).

Here is a complete example:

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;
}

For more info on packages and symbol tables, see the perlmod man page.

屋檐 2024-08-01 05:57:54

取决于您是否指的是任何课程,或者您是否正在实现自己的课程。 对于后者,我使用 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

Depends if you mean, any class, or if you were implementing your own. For the latter, I use Moose, which offers a very clean syntax for these features. From the cookbook:

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
绝影如岚 2024-08-01 05:57:54

您可能需要 Class::Inspector->methods('Your::Class')。

纳夫说道。

You probably want Class::Inspector->methods('Your::Class').

Nuff said.

苦行僧 2024-08-01 05:57:54

我会把它留在这里,以备我忘记时使用。 这是极其强大的; 可惜的是,它太偏僻了,大多数 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::;

I'll just leave this here for when I forget it. This is extremely powerful; too bad it is so out of the way that most Perl programmers never get to experience it.

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::;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文