23

Weźmy następujący przykład skrypt:Dlaczego metoda @staticmethod nie jest zachowywana na różnych klasach, gdy @classmethod jest?

class A(object): 
    @classmethod 
    def one(cls): 
     print("I am class") 

    @staticmethod 
    def two(): 
     print("I am static") 


class B(object): 
    one = A.one 
    two = A.two 


B.one() 
B.two() 

Kiedy uruchomić ten skrypt Pythona 2.7.11 uzyskać:

I am class 
Traceback (most recent call last): 
    File "test.py", line 17, in <module> 
    B.two() 
TypeError: unbound method two() must be called with B instance as first argument (got nothing instead) 

Wydaje się, że dekorator @classmethod jest zachowana w całej klasy, ale @ staticmethod nie jest.

Python 3.4 zachowuje się zgodnie z oczekiwaniami:

I am class 
I am static 

Dlaczego python2 niezabezpieczanie @staticmethod, i czy istnieje obejście tego problemu?

edytuj: pobranie dwóch z klasy (i zachowanie metody @staticmethod) wydaje się działać, ale nadal wydaje mi się to dziwne.

Odpowiedz

16

classmethod i staticmethod są opisowe, a żadna z nich nie robią tego, co można się spodziewać, nie tylko staticmethod.

Po uzyskaniu dostępu A.one, to stworzenie metody związanej na A, to co, że atrybutem B, ale ponieważ to wiąże się z A The cls argumentem zawsze będzie A, nawet jeśli nazywają B.one (jest to przypadek zarówno w Pythonie 2, jak i Pythonie 3. wszędzie jest źle.

Po uzyskaniu dostępu A.two, to powrót surowego obiektu funkcyjnego (deskryptor staticmethod nie trzeba robić nic specjalnego oprócz zapobiegania wiązania, które przechodzą self lub cls, więc po prostu wraca co owinięty). Ale ten surowy obiekt funkcji jest następnie dołączany do B jako niezwiązana metoda instancji, ponieważ bez zawijania staticmethod jest tak, jak zdefiniowałeś to normalnie.

Powodem, dla którego ten ostatni działa w Pythonie 3, jest to, że Python 3 nie ma pojęcia metod niezwiązanych. Posiada funkcje (do których dostęp uzyskuje się poprzez instancję klasy związanej) i powiązane metody, gdzie Python 2 ma funkcje, niezwiązane metody i związane metody.

Metody niezwiązane sprawdzają, czy są wywoływane z obiektem o poprawnym typie, a tym samym o błędzie. Proste funkcje wymagają tylko poprawnej liczby argumentów.

Dekorator staticmethod w Pythonie 3 wciąż zwraca surowy obiekt funkcji, ale w Pythonie 3 jest to w porządku; ponieważ nie jest to specjalny niezwiązany obiekt metody, jeśli wywołasz go na samej klasie, to jest to tak jak funkcja z wyświetlaniem nazw, a nie jakakolwiek metoda.chcesz zobaczyć problem, jeśli starali się zrobić:

B().two() 

chociaż, bo to uczyni metodę związaną z tym wystąpieniem B i funkcji two, przekazując dodatkowy argument (self), że nie two zaakceptować. Zasadniczo, na Pythonie 3, staticmethod jest wygodą pozwalającą wywołać funkcję na instancjach bez powodowania wiązania, ale jeśli tylko wywołasz tę funkcję odwołując się do samej klasy, nie jest to potrzebne, ponieważ jest to zwykła funkcja, a nie Python 2 "metoda niezwiązana".

Jeśli miał jakieś powody, aby wykonać tę kopię (normalnie sugeruję dziedziczenie z A, ale cokolwiek), i chcesz, aby upewnić się uzyskać deskryptora owinięte w wersji z funkcji, co nie daje Ci deskryptor kiedy dostępne na A, można ominąć przez protokół deskryptora bezpośredniego dostępu A „s __dict__:

class B(object): 
    one = A.__dict__['one'] 
    two = A.__dict__['two'] 

kopiując bezpośrednio ze słownika klasy, magia protokół deskryptor nie jest wywoływany, a otrzymasz staticmethod i classmethod opakowane wersje one i two.

+0

Wielkie dzięki za informacyjną odpowiedź! Przypadek użycia to pakiet testowy: https://github.com/al4/python-checkrunner/blob/master/tests/test_checkrunner.py Chciałem ponownie użyć tych funkcji i pogrupować je w jedną klasę, aby uniknąć skarg składanych przez PyCharm. dekoratorzy spoza klasy. Najlepszym podejściem jest, jak sądzę, ręczne wywołanie staticmethod z klasą "sub". –

+1

@AlexForbes: W takim przypadku zaimplementowałbym klasę wspólnych funkcji i użyłbym jej jako mixin w klasach, które ich potrzebują. 'class CommonTests (object): ... define jeden, two ...', a następnie miksuj go przez dziedziczenie wielu: 'class A (unittests.TestCase, CommonTests): ... Konkretne testy ...', a następnie 'klasa B (unittests.TestCase, CommonTests): ... specyficzne testy B ...'. Teraz wszystkie metody CommonTests są dziedziczone automatycznie w 'A' i' B', bez konieczności przypisywania ich indywidualnie. Klasa mixin nie dziedziczy po 'TestCase', więc nie będzie testowana samodzielnie. – ShadowRanger

4

ZASTRZEŻENIE: To nie jest odpowiedź, ale nie pasuje również do formatu komentarza.

Należy zauważyć, że z python2 @classmethod NIE poprawnie zachowane całej klasy albo. W poniższym kodzie, wywołanie B.one() prac tak, jakby został wywołany przez klasy A:

$ cat test.py 
class A(object): 
    @classmethod 
    def one(cls): 
     print("I am class", cls.__name__) 

class A2(A): 
    pass 

class B(object): 
    one = A.one 


A.one() 
A2.one() 
B.one() 

$ python2 test.py 
('I am class', 'A') 
('I am class', 'A2') 
('I am class', 'A')