代码之家  ›  专栏  ›  技术社区  ›  BuddyJoe

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

  •  9
  • BuddyJoe  · 技术社区  · 16 年前

    如何在Perl中循环类的所有方法?有没有关于Perl自省或反思的好的在线参考?

    6 回复  |  直到 7 年前
        1
  •  13
  •   HoldOffHunger Lux    7 年前

    托德·加德纳提出的使用驼鹿的建议是一个很好的建议,但是他选择的示例代码并没有很好的帮助。

    如果你正在使用类检查一个非驼鹿,你可以这样做:

    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。类::mop(mop=元对象协议)是moose构建的基础。如果你正在学习非驼鹿课程,用驼鹿反省并没有什么好处。

    如果你愿意,你可以 use Moose () Moose::Meta::Class->initialize 而不是CMOP。

        2
  •  10
  •   brian d foy    16 年前

    使用已经提供的答案,您可以很容易地获得类的定义方法列表。然而,Perl是一种动态语言,这意味着以后可以定义更多的方法。实际上没有一种方法可以获得任何特定类将处理的所有方法的列表。关于这类事情的更多细节,我在 Mastering Perl .

    人们给你的(和赞成的)答案没有告诉你限制。

    亚当提到他的 Class::Inspector ,但它实际上不起作用,因为它试图做动态语言不做的事情(这是静态的):例如,这里有一个片段,class::inspector不返回任何方法,但我仍然可以调用 VERSION 方法(以及 isa 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 (仍然失踪 版本 , 伊萨 可以 ):

    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 也有同样的局限性。

    不是每个模块都要使用自动加载,但它也不是一个晦涩或罕见的功能。如果您不介意丢失某些方法,那么class::inspector或class::mop可能是正常的。它不会给你一个列表,列出在任何情况下你都可以调用一个类或对象的每个方法。

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

    if( eval { $object->can( 'method_name' ) } )
        {
        $object->( @args );
        }
    
        3
  •  5
  •   trendels    16 年前

    在一般情况下,您必须检查符号表(除非您使用驼鹿)。例如,要列出在 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 人页。

        4
  •  3
  •   Todd Gardner    16 年前

    取决于您的意思是,任何类,或者您是否正在实现自己的类。对于后者,我使用 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
    
        5
  •  3
  •   Adam Kennedy    16 年前

    您可能需要class::inspector->方法(“your::class”)。

    Nuff说。

        6
  •  0
  •   Dmytro    8 年前

    我把这个留着等我忘了再说。这是非常强大的;太糟糕了,它太离经叛道了,以至于大多数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::;