How do I loop over all the methods of a class in P

2019-04-06 04:59发布

问题:

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

回答1:

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.



回答2:

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


回答3:

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.



回答4:

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


回答5:

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

Nuff said.



回答6:

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