Come si collegano tutti i metodi di una classe in Perl? Ci sono dei buoni riferimenti online sull'introspezione o riflessione Perl?Come posso eseguire il loop su tutti i metodi di una classe in Perl?
risposta
La raccomandazione che Todd Gardner ha dato all'utilizzo di Moose è buona, ma il codice di esempio che ha scelto non è molto utile.
Se stai ispezionando un non-Moose utilizzando la classe, si farebbe qualcosa di simile:
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";
}
Vedere la Class::MOP::Class docs per maggiori dettagli su come fare introspezione.
Noterete che ho usato Class :: MOP invece di Moose. Class :: MOP (MOP = Meta-Object Protocol) è la base su cui Moose si basa. Se lavori con classi non Moose, usare Moose per introspettarti non ti porta nulla.
Se si desidera, è possibile use Moose()
e Moose::Meta::Class->initialize
anziché CMOP.
+1: È pulito, non sapevo che si potesse usare Class :: MOP in questo modo. –
Spiego i limiti di questo nella mia risposta. Anche con una classe vuota, non riesce a prendere i metodi da UNIVERSAL e non può prelevarne alcuno da AUTOLOAD. –
@brian d foy: L'uso di AUTOLOAD è sbagliato, mmmkay? – HoldOffHunger
Dipende se si intende qualsiasi classe o se si sta implementando la propria. Per quest'ultimo, utilizzo Moose, che offre una sintassi molto pulita per queste funzionalità. Dal ricettario:
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
Nota che Moose non troverà alcun attributo a meno che la classe che stai introspettando sia stata definita usando Moose in primo luogo. Tuttavia, l'introspezione del metodo, così come la maggior parte degli altri bit, funzionerà perfettamente. –
Nel caso generale, è necessario ispezionare la tabella dei simboli (a meno che non si usi Alci). Ad esempio, per elencare i metodi definiti nel pacchetto IO::File
:
use IO::File;
no strict 'refs';
print join ', ', grep { defined &{"IO::File::$_"} } keys %{IO::File::};
L'hash %{IO::File::}
è la tabella dei simboli del IO::File package
, e il grep
filtri su partite non subroutine (ad esempio variabili del pacchetto).
Per estendere questo metodo ai metodi ereditati, è necessario cercare ricorsivamente le tabelle dei simboli delle classi parent (@IO::File::ISA
).
Ecco un esempio 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;
}
Per maggiori informazioni su pacchetti e tabelle di simboli, vedere la pagina man perlmod.
Non tutti i metodi devono essere definiti nella tabella dei simboli e manca UNIVERSAL. –
Mi stavo chiedendo, se una qualsiasi delle soluzioni per questa domanda, in particolare questa qui, genera una nuova istanza di oggetto ogni volta che viene chiamato il codice e se viene riciclata o ogni istanza rimane in memoria fino alla fine del programma. Poiché l'uso della riflessione/introspezione in altri linguaggi come .NET e Java, deve fare attenzione a dove istanziare il riferimento all'oggetto di classe altrimenti può ottenere nuove istanze per chiamata. – David
Probabilmente vuoi i metodi Class :: Inspector-> ('Your :: Class').
Nuff ha detto.
È possibile ottenere facilmente un elenco dei metodi definiti di una classe utilizzando le risposte già fornite. Tuttavia, Perl è un linguaggio dinamico, il che significa che più metodi possono essere definiti in seguito. Non c'è davvero un modo per ottenere un elenco di tutti i metodi a cui una particolare classe gestirà. Per molti più dettagli su questo genere di cose, ho alcuni capitoli in Mastering Perl.
Le persone stanno dando risposte (e upvoting) senza dirti quali sono i limiti.
Adam menziona il suo Class::Inspector, ma in realtà non funziona perché sta provando a fare qualcosa che un linguaggio dinamico non fa (e questo è statico :) Ad esempio, ecco uno snippet dove Class :: Inspector non restituisce metodi , ma posso ancora chiamare il metodo VERSION
(così come isa
e 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";
Ecco un altro caso in cui posso chiamare qualsiasi metodo che mi piace, ma Class :: ispettore restituisce solo AUTOLOAD
(e mancano ancora VERSION
, isa
e 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, tutti sembrano ignorare UNIVERSAL, probabilmente perché non lo gestiscono esplicitamente poiché è solo virtualmente in @ISA. Posso aggiungere un metodo debug
per ogni classe, e Classe ispettore :: manca ancora anche se è un metodo definito:
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 ha le stesse limitazioni.
Non tutti i moduli useranno AUTOLOAD, ma non è nemmeno una caratteristica oscura o rara. Se non ti dispiace che mancherai alcuni dei metodi, Class :: Inspector o Class :: MOP potrebbe andare bene. Semplicemente non ti fornirà un elenco di tutti i metodi che puoi chiamare su una classe o su un oggetto in ogni caso.
Se si dispone di una classe o di un oggetto e si desidera sapere se è possibile chiamare un metodo particolare, utilizzare può(). Avvolgerlo in un blocco eval così può può chiamare in grado di() su cose che non sono anche gli oggetti per ottenere ancora indietro falsa, invece della morte, in questi casi:
if(eval { $object->can('method_name') })
{
$object->(@args);
}
mi limiterò a lasciare questo qui per quando lo dimentico. Questo è estremamente potente; peccato che sia così fuori mano che la maggior parte dei programmatori Perl non riesca mai a provarlo.
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::;
Per la prima domanda, vedi: http://stackoverflow.com/questions/910430/how-do-i-list-available-methods-on-a-given-object-or-package-in -perl – molf