2009-06-20 16 views
9

¿Cómo se repiten todos los métodos de una clase en Perl? ¿Hay alguna buena referencia en línea para la introspección o reflexión de Perl?¿Cómo repito todos los métodos de una clase en Perl?

+0

Por primera cuestión, véase: http://stackoverflow.com/questions/910430/how-do-i-list-available-methods-on-a-given-object-or-package-in -perl – molf

Respuesta

13

La recomendación que dio Todd Gardner para usar Moose es buena, pero el código de ejemplo que eligió no es muy útil.

Si está inspeccionando un no alces usando la clase, usted haría algo como esto:

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

Véase el Class::MOP::Class docs para más detalles sobre cómo hacer introspección.

También notará que utilicé Class :: MOP en lugar de Moose. Class :: MOP (MOP = Meta-Object Protocol) es la base sobre la cual Moose construye. Si trabajas con clases que no son de Moose, usar Moose para introspect no te gana nada.

Si desea, puede use Moose() y Moose::Meta::Class->initialize en lugar de CMOP.

+0

+1: Eso está bien, no sabía que podrías usar Class :: MOP así. –

+1

Explicaré las limitaciones de esto en mi respuesta. Incluso con una clase vacía no puede recoger los métodos de UNIVERSAL, y no puede recoger ninguno de AUTOLOAD. –

+0

@brian d foy: Usar AUTOLOAD es malo, mmmkay? – HoldOffHunger

3

Depende si quiere decir, cualquier clase, o si estaba implementando la suya propia. Para este último, utilizo Moose, que ofrece una sintaxis muy clara para estas características. Del libro de cocina:

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

Tenga en cuenta que Moose no encontrará ningún atributo a menos que la clase que está introspectando se definió utilizando Moose en primer lugar. Sin embargo, la introspección del método, así como la mayoría de los demás bits, funcionará bien. –

5

En el caso general, tendrá que inspeccionar la tabla de símbolos (a menos que use Moose). Por ejemplo, para una lista de los métodos definidos en el paquete IO::File:

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

El hash %{IO::File::} es la tabla de símbolos del IO::File package y la grep los filtros a cabo entradas no subrutina (por ejemplo, variables de paquete).

Para extender esto y que incluya métodos heredados, debe buscar de forma recursiva las tablas de símbolos de las clases principales (@IO::File::ISA).

Aquí es un ejemplo completo:

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

Para obtener más información sobre los paquetes y tablas de símbolos, ver la página perlmod hombre.

+0

No todos los métodos tienen que definirse en la tabla de símbolos, y te falta UNIVERSAL. –

+0

Me preguntaba si alguna de las soluciones para esta pregunta, especialmente esta aquí, genera una nueva instancia de objeto cada vez que se llama al código y si se recicla o cada instancia permanece en la memoria hasta que finaliza el programa. Debido a que el uso de la reflexión/introspección en algunos otros lenguajes como .NET y Java, debe tener cuidado en dónde instanciar la referencia del objeto de clase, sino que puede obtener nuevas instancias por llamada. – David

3

Probablemente desee Class :: Inspector-> methods ('Your :: Class').

Nuff dijo.

10

Puede obtener fácilmente una lista de los métodos definidos de una clase utilizando las respuestas ya proporcionadas. Sin embargo, Perl es un lenguaje dinámico, lo que significa que más métodos se pueden definir más adelante. Realmente no hay una forma de obtener una lista de todos los métodos a los que se encargará una clase en particular. Para obtener más detalles sobre este tipo de cosas, tengo algunos capítulos en Mastering Perl.

Las personas te dan (y suben) respuestas sin decirte las limitaciones.

Adam menciona su Class::Inspector, pero realmente no funciona porque está tratando de hacer algo que un lenguaje dinámico no hace (y eso es estático :) Por ejemplo, aquí hay un fragmento donde Class :: Inspector no devuelve ningún método , pero todavía puedo llamar a la VERSION método (así como isa y 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"; 

Aquí hay otro caso en el que puedo llamar a cualquier método que me gusta, pero Clase :: inspector sólo devuelve AUTOLOAD (y todavía falta VERSION , isa y 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"; 

Curiosamente, todo el mundo parece ignorar UNIVERSAL, probablemente porque no lo manejan explícitamente, ya que es prácticamente solo en @ISA. Puedo añadir un método debug a cada clase, y la clase :: inspector sigue sin ver que a pesar de que se trata de un método definido:

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 tiene las mismas limitaciones.

No todos los módulos van a utilizar AUTOLOAD, pero tampoco es una característica oscura o rara. Si no te importa que te pierdas algunos de los métodos, Class :: Inspector o Class :: MOP podrían estar bien. Simplemente no le dará una lista de todos los métodos que puede invocar en una clase o un objeto en todos los casos.

Si tiene una clase o un objeto y desea saber si puede llamar a un método en particular, use can(). Envolverlo en un bloque eval por lo que puede puede llamar puede() en cosas que no son incluso objetos que todavía volver falsa, en lugar de la muerte, en esos casos:

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

Voy a dejar esto aquí para cuando lo olvido Esto es extremadamente poderoso; Es una pena que la mayoría de los programadores de Perl nunca lo experimenten.

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