Как мне перебрать все методы класса в Perl? - PullRequest
9 голосов
/ 20 июня 2009

Как вы перебираете все методы класса в Perl? Есть ли в Интернете хорошие ссылки на самоанализ Perl или рефлексию?

Ответы [ 6 ]

13 голосов
/ 20 июня 2009

Рекомендация Тодда Гарднера по использованию 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.

10 голосов
/ 21 июня 2009

Вы можете легко получить список определенных методов класса, используя ответы, уже предоставленные. Тем не менее, Perl является динамическим языком, что означает, что другие методы могут быть определены позже. На самом деле нет способа получить список всех методов, с которыми будет работать какой-либо конкретный класс. Для более подробной информации о подобных вещах у меня есть несколько глав в Мастеринг 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 (и все еще отсутствует 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";

Класс :: MOP имеет те же ограничения.

Не каждый модуль будет использовать AUTOLOAD, но это не редкая и не редкая функция. Если вы не возражаете против того, что вы пропустите некоторые методы, тогда Class :: Inspector или Class :: MOP могут подойти. Это просто не даст вам список всех методов, которые вы можете вызывать для класса или объекта в каждом случае.

Если у вас есть класс или объект, и вы хотите знать, можете ли вы вызвать конкретный метод, используйте can (). Оберните его в блок eval, чтобы функция can могла вызывать can () для объектов, которые даже не являются объектами, и возвращают значение false, вместо смерти, в следующих случаях:

if( eval { $object->can( 'method_name' ) } )
    {
    $object->( @args );
    }
5 голосов
/ 20 июня 2009

В общем случае вам придется проверять таблицу символов (если вы не используете 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 .

3 голосов
/ 21 июня 2009

Возможно, вам нужны методы Class :: Inspector-> ('Your :: Class').

Нуфф сказал.

3 голосов
/ 20 июня 2009

Зависит от того, имеете ли вы в виду какой-либо класс или реализуете свой собственный. Для последнего я использую 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
0 голосов
/ 12 ноября 2016

Я просто оставлю это здесь, когда забуду. Это очень сильно; Жаль, что это настолько необычно, что большинство программистов на 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::;
...