2013-04-26 16 views
5

Dlaczego jest tak, że w poniższym kodzie, używając zmiennej klasy jako wynikami metoda wskaźnika w niezwiązanego błędu metody, przy użyciu zmiennej zwykłego działa prawidłowo:Wskaźniki do metod statycznych w Pythonie

class Cmd: 
    cmd = None 

    @staticmethod 
    def cmdOne(): 
     print 'cmd one' 

    @staticmethod 
    def cmdTwo(): 
     print 'cmd two' 

def main(): 
    cmd = Cmd.cmdOne 
    cmd() # works fine 

    Cmd.cmd = Cmd.cmdOne   
    Cmd.cmd() # unbound error !! 

if __name__=="__main__": 
    main() 

Pełna błąd :

TypeError: unbound method cmdOne() must be called with Cmd instance as 
      first argument (got nothing instead) 

Odpowiedz

2

Lubię oglądać to zachowanie od "oddolnego".

Funkcja w języku Python działa jako "descriptor object". Jako taki ma on metodę __get__().

Dostęp do odczytu do atrybutu klasy, który ma taką metodę __get__(), jest przekierowywany do tej metody. Dostęp do atrybutu do klasy jest wykonywany jako attribute.__get__(None, containing_class), podczas gdy atrybut dostępu do instancji jest mapowany na attribute.__get__(instance, containing_class).

Zadaniem metody z funkcji jest zawijanie funkcji w obiekcie metody, który owija parametr self - dla przypadku dostępu do atrybutu do instancji. Nazywa się to metodą związaną.

W przypadku dostępu do atrybutu klasy w wersji 2.x funkcja __get__() zwraca niezwiązane opakowanie metody, a jako learned today na 3.x zwraca wartość. (Zauważ, że mechanizm __get__() nadal istnieje w 3.x, ale funkcja właśnie się zwraca.) To prawie to samo, jeśli spojrzysz na to, jak jest wywoływany, ale niezwiązane opakowanie metody sprawdza dodatkowo poprawny typ argumentu self.

Wywołanie tworzy obiekt, którego wywołanie __get__() ma na celu zwrócenie pierwotnie podanego obiektu, aby cofnąć opisane zachowanie. Tak działa HYRY's trick: atrybut umożliwia cofnięcie pakowania staticmethod(), wywołanie robi to ponownie, tak że atrybut "nowy" ma taki sam status jak stary, chociaż w tym przypadku wydaje się, że staticmethod() został zastosowany dwukrotnie (ale tak naprawdę nie jest " t).

(BTW: To działa nawet w tym dziwnym kontekście.

s = staticmethod(8) 
t = s.__get__(None, 2) # gives 8 

chociaż 8 nie jest funkcją i 2 nie jest klasą)

w swoim pytaniu, masz dwie sytuacje:

cmd = Cmd.cmdOne 
cmd() # works fine 

dostęp klasę i prosi o jego atrybutem cmdOne, a staticmethod() obiektu. Jest to sprawdzane poprzez jego __get__() i zwraca pierwotną funkcję, która jest następnie wywoływana. Właśnie dlatego działa dobrze.

robi to samo, ale następnie przypisuje tę funkcję do Cmd.cmd. Następny wiersz to dostęp do atrybutu - który ponownie wywołuje funkcję __get__() i zwraca w ten sposób niezwiązaną metodę, która musi zostać wywołana z poprawnym obiektem self jako pierwszym argumentem.

3

trzeba użyć staticmethod() przekształcić funkcję:

Cmd.cmd = staticmethod(Cmd.cmdOne) 
+0

Nie odpowiada na pytanie. – martineau

3

Y Zachowujesz się w "niezwiązanych metodach" w Pythonie 2.x. Zasadniczo, w Pythonie 2.x, gdy uzyskasz atrybut klasy (np. W tym przypadku Cmd.cmd), a wartość jest funkcją, wówczas klasa "opakowuje" tę funkcję w specjalny obiekt "niezwiązanej metody", ponieważ załóżmy, że atrybuty klas, które są funkcjami i nie są ozdobione staticmethod lub classmethod, mają być metodami instancji (niepoprawne założenie w tym przypadku). Ta niezwiązana metoda oczekuje wywołania argumentu, nawet jeśli w tym przypadku funkcja bazowa nie oczekuje argumentu.

Zachowanie to jest wyjaśnione w language reference (rozdziału "klas")

When a class attribute reference (for class C, say) would yield a user-defined function object or [...], it is transformed into an unbound user-defined method object whose im_class attribute is C.

(w sekcji "Metody użytkownika")

When a user-defined method object is created by retrieving a user-defined function object from a class, its im_self attribute is None and the method object is said to be unbound.

[ ...]

When an unbound user-defined method object is called, the underlying function (im_func) is called, with the restriction that the first argument must be an instance of the proper class (im_class) or of a derived class thereof.

To powoduje błąd, który widzisz.

Można wyraźnie odzyskać funkcji podstawowej z przedmiotu metody i zadzwonić, że (ale to nie jest oczywiście idealna do trzeba to zrobić):

Cmd.cmd.im_func() 

Zauważ, że Python 3.x got rid of unbound methods i Twój kod będzie działa dobrze na Pythonie 3.x

+0

Dokładniej, sama funkcja "opakowuje" się w obiekt "niezwiązanej metody": dostęp do niej wywołuje funkcję '__get __()', która tworzy opakowanie. – glglgl

Powiązane problemy