2013-08-22 26 views
6

Czy można uzyskać wszystkie prawidłowe metody dla konkretnej klasy Perla?Czy można uzyskać wszystkie prawidłowe metody dla konkretnej klasy Perla?

Próbuję manipulować tabelą symboli klasy i uzyskać wszystkie jego metody. Zauważyłem, że mogę oddzielić podprocedury od podprogramów poprzez $obj->can($method), ale to nie robi dokładnie tego, co myślę, że robi.

następujące zwroty:

subroutine, Property, croak, Group, confess, carp, File 

Jednak subroutine nie jest to metoda, (tylko podprogram) i croak, confess, a były importowane do mojego pakietu.

Co naprawdę chcę wydrukować to:

Property,Group, File 

Ale wezmę:

subroutine, Property,Group, File 

Poniżej jest mój program:

#! /usr/bin/env perl 

use strict; 
use warnings; 
use feature qw(say); 

my $sections = Section_group->new; 
say join ", ", $sections->Sections; 

package Section_group; 
use Carp; 

sub new  { 
    return bless {}, shift; 
} 

sub Add { 
    my $self    = shift; 
    my $section    = shift; 
} 

sub Sections { 
    my $self    = shift; 

    my @sections; 
    for my $symbol (keys %Section_group::) { 
     next if $symbol eq "new"; # This is a constructor 
     next if $symbol eq "Add"; # Not interested in this method 
     next if $symbol eq "Sections";  # This is it's own method 
     push @sections, $symbol if $self->can($symbol); 
    } 

    return wantarray ? @sections : \@sections; 
} 

sub subroutine { 
    my $param1    = shift; 
    my $param2    = shift; 
} 

sub Group { 
    my $self    = shift; 
    my $section    = shift; 
} 

sub File { 
    my $self    = shift; 
    my $section    = shift; 
} 

sub Property { 
    my $self    = shift; 
    my $section    = shift; 
} 

Odpowiedz

6

Jest to dość trywialne. Chcemy tylko zachować te nazwy podrzędne, które zostały pierwotnie zdefiniowane w naszym pakiecie. Każde CV (wartość kodu) ma wskaźnik do pakietu, w którym został zdefiniowany. Dzięki B możemy zbadać że:

use B(); 

... 

if (my $coderef = $self->can($symbol)) { 
    my $cv = B::svref_2object $coderef; 
    push @sections, $symbol if $cv->STASH->NAME eq __PACKAGE__; 
} 

# Output as wanted 

Oznacza to, że możemy wykonać za pomocą introspekcji svref_2object. Zwraca to obiekt Perla reprezentujący wewnętrzną strukturę danych perla.

Jeśli zajrzymy do coderef, otrzymamy B::CV object, który reprezentuje wewnętrzny CV. Pole STASH w CV wskazuje na Skrytkę, gdzie zostało zdefiniowane. Jak wiadomo, Skrytka jest po prostu specjalną mieszanką (wewnętrznie reprezentowaną jako HV), więc zwraca wartość . Pole NAME zawiera pełną nazwę pakietu, jeśli HV to skrytka, a nie zwykły skrót.

Teraz mamy wszystkie informacje, których potrzebujemy i możemy porównać nazwę żądanego pakietu z nazwą skrytki coderef.

Oczywiście, jest to uproszczone, a będziesz chciał powtórzyć przez @ISA dla klas ogólnych.


Nikt nie lubi zanieczyszczonych przestrzeni nazw. Na szczęście istnieją moduły, które usuwają obce symbole ze skrytki, np. namespace::clean. Nie stanowi to problemu, gdy życiorysy wszystkich subskrybujących wywołań są znane w czasie kompilacji.

+3

Z drugiej strony, czasami ludzie faktycznie importują metodę z innego pakietu (np. 'Use Exporter 'import';' w przeciwieństwie do '@ISA = qw (Exporter);'). – cjm

+0

@cjm Oczywiście, ale domyślam się, że to nie zdarza się często i może nawet być antypaplanetem. Mamy nadzieję, że większość ludzi oddzieliła mentalnie programowanie proceduralne (z importowaniem) od OOP (co rozwiązuje wiele problemów związanych z przestrzenią nazw). Nie ma sposobu, aby dowiedzieć się, czy zaimportowany okręt podwodny miał być metodą, więc musi to być wystarczająco dobre. (Zaczekaj, może, jeśli mamy dostęp do atrybutu ': ​​method' ...) – amon

+0

_ To jest dość trywialne. Trywialny? Budujesz generatory tachionów w weekendy? Perldoc na ** svref_2object **: _ Zwraca referencję do dowolnej wartości Perla i zamienia odwołaną wartość na obiekt w odpowiedniej klasie pochodnej B :: OP lub B :: SV. Nie mam pojęcia, co to jest to mówi. Jednak to działa. Sądzę, że nadszedł czas, aby zagłębić się w wcześniej niedopasowane i poprawić nieco moją grę Perla. Albo to, albo naucz się Pythona. –

6

Co próbujesz zrobić? Dlaczego ma znaczenie, w jaki sposób klasa zdefiniowała lub wdrożyła metodę, na którą reaguje?

Perl jest językiem dynamicznym, co oznacza, że ​​metody wcale nie muszą istnieć. W przypadku AUTOLOAD metoda może być idealnie w porządku i wywoływalna, ale nigdy nie pojawia się w tabeli symboli. Dobry interfejs sprawiłby, że w takich przypadkach zadziałałby can, ale mogą zdarzyć się sytuacje, w których klasa lub obiekt decyduje się odpowiedzieć na to z błędem.

Moduł Package::Stash może pomóc w znalezieniu zdefiniowanych podprogramów w określonym obszarze nazw, ale jak można powiedzieć, mogą one nie być zdefiniowane w tym samym pliku. Metody w klasie mogą pochodzić z dziedziczonej klasy. Jeśli zależy ci na tym, skąd pochodzą, prawdopodobnie robisz to źle.

+0

Mam klasy o nazwie _Sections_, który reprezentuje różne typy sekcji w moim pliku kontrolnym, który jest w formacie Windows INI. Każda sekcja ma parametry, ale parametry są różne dla każdego rodzaju Sekcji. (Obecnie jest ich pięć). Każdy typ sekcji jest podklasą sekcji. Mam inną klasę, która zawiera dla mnie całą listę różnych sekcji. Każdy typ sekcji otrzymuje nową metodę w tej ostatniej klasie. W ten sposób cała moja definicja tego pliku INI jest w jednym obiekcie. Jedna metoda na mojej klasie dostaje mi nazwy wszystkich innych podklas. –

+0

Używałem AUTOLOAD, ale teraz go unikam. Użycie 'use strict' sprawia, że ​​programowanie jest lepsze. Używanie struktur referencyjnych usuwa to bezpieczeństwo. Mogę mieć '$ foo -> {BAR}' w jednym miejscu i '$ foo -> {BRA}' w innym, i ścisłe nie mogę tego złapać. OO przywraca to bezpieczeństwo. Jeśli moja metoda to $ foo-> Bar, wywołanie $ foo-> Bra spowoduje awarię mojego programu. AUTOLOAD łamie ten projekt. Obie są teraz prawdziwymi metodami. To również nadaje się do gorszej ogólnej konstrukcji. Mogę go skrzywić za pomocą AUTOLOAD. Nie muszę o tym myśleć. Moja poprzednia wersja tego programu korzystała z AUTOLOAD i może być trudny do debugowania dzięki AUTOLOAD. –

+0

Dodałbym wszystkie te wyjaśnienia do pytania :) –

Powiązane problemy