Wywołanie klasy staticmethod wewnątrz ciała klasy?

Kiedy próbuję użyć statycznej metody z wewnątrz ciała klasy i zdefiniować statyczną metodę używając wbudowanej funkcji staticmethod jako dekoratora, jak to:

class Klass(object):

    @staticmethod  # use as decorator
    def _stat_func():
        return 42

    _ANS = _stat_func()  # call the staticmethod

    def method(self):
        ret = Klass._stat_func() + Klass._ANS
        return ret

Dostaję następujący błąd:

Traceback (most recent call last):<br>
  File "call_staticmethod.py", line 1, in <module>
    class Klass(object): 
  File "call_staticmethod.py", line 7, in Klass
    _ANS = _stat_func() 
  TypeError: 'staticmethod' object is not callable

rozumiem, dlaczego tak się dzieje (Wiązanie deskryptora), i mogę obejść to, ręcznie konwertując _stat_func() do statycznego methoda po jego ostatnim użyciu, tak:

class Klass(object):

    def _stat_func():
        return 42

    _ANS = _stat_func()  # use the non-staticmethod version

    _stat_func = staticmethod(_stat_func)  # convert function to a static method

    def method(self):
        ret = Klass._stat_func() + Klass._ANS
        return ret

Więc moje pytanie brzmi:

czy są lepsze, jak w czystszym czy bardziej "Pythoniczne", sposoby na to?

Author: martineau, 2012-10-04

6 answers

staticmethod obiekty najwyraźniej mają atrybut __func__ przechowujący oryginalną funkcję raw (ma to sens, że musiały). Więc to zadziała:

class Klass(object):

    @staticmethod  # use as decorator
    def stat_func():
        return 42

    _ANS = stat_func.__func__()  # call the staticmethod

    def method(self):
        ret = Klass.stat_func()
        return ret

Na marginesie, chociaż podejrzewałem, że obiekt staticmethod ma jakiś atrybut przechowujący oryginalną funkcję, nie miałem pojęcia o szczegółach. W duchu uczenia kogoś do łowienia ryb, a nie dawania im ryby, to jest to, co zrobiłem, aby zbadać i dowiedzieć się, że (dosłownie C & P z mojego Pythona sesja): {]}

>>> class Foo(object):
    @staticmethod
    def foo():
        return 3
    global z
    z = foo

>>> z
<staticmethod object at 0x0000000002E40558>
>>> Foo.foo
<function foo at 0x0000000002E3CBA8>
>>> dir(z)
['__class__', '__delattr__', '__doc__', '__format__', '__func__', '__get__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>> z.__func__
<function foo at 0x0000000002E3CBA8>

Podobne rodzaje kopania w interaktywnej sesji (dir jest bardzo pomocne) często mogą rozwiązać tego rodzaju pytania bardzo szybko.

 112
Author: Ben,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-03 23:24:28

Tak wolę:

class Klass(object):

    @staticmethod
    def stat_func():
        return 42

    _ANS = stat_func.__func__()

    def method(self):
        return self.__class__.stat_func() + self.__class__._ANS

Wolę to rozwiązanie od Klass.stat_func, ze względu na zasadę suchości. Przypomina mi powód, dla którego jest nowy super() w Pythonie 3:)

Ale Zgadzam się z innymi, zwykle najlepszym wyborem jest zdefiniowanie funkcji poziomu modułu.

Na przykład w przypadku funkcji @staticmethod rekurencja może nie wyglądać zbyt dobrze (trzeba by złamać zasadę DRY wywołując Klass.stat_func wewnątrz Klass.stat_func). To dlatego, że nie masz odniesienie do self wewnątrz metody statycznej. Dzięki funkcji poziomu modułu wszystko będzie wyglądać dobrze.

 9
Author: Jan Vorcak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-10 21:13:31

Jest to spowodowane tym, że staticmethod jest deskryptorem i wymaga atrybutu fetch na poziomie klasy, aby wykonać protokół deskryptora i uzyskać true callable.

Z kodu źródłowego:

Może być wywołana albo na klasie (np. C.f()) albo na instancji (np. C().f()); instancja jest ignorowana z wyjątkiem jej klasy.

Ale nie bezpośrednio z wnętrza klasy podczas jej definiowania.

Ale jak wspomniał jeden z komentatorów, nie jest to tak naprawdę Projekt "Pythonic" w ogóle. Wystarczy użyć funkcji poziomu modułu.

 8
Author: Keith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-04 00:00:46

Co z tym rozwiązaniem? Nie opiera się na znajomości implementacji @staticmethod dekoratora. Wewnętrzna Klasa StaticMethod gra jako kontener statycznych funkcji inicjalizacyjnych.

class Klass(object):

    class StaticMethod:
        @staticmethod  # use as decorator
        def _stat_func():
            return 42

    _ANS = StaticMethod._stat_func()  # call the staticmethod

    def method(self):
        ret = self.StaticMethod._stat_func() + Klass._ANS
        return ret
 7
Author: schatten,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-15 23:29:10

A co z wprowadzaniem atrybutu klasy po definicji klasy?

class Klass(object):

    @staticmethod  # use as decorator
    def stat_func():
        return 42

    def method(self):
        ret = Klass.stat_func()
        return ret

Klass._ANS = Klass.stat_func()  # inject the class attribute with static method value
 6
Author: Pedro Romano,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-03 23:21:05

Zgodnie z poniższym blogiem, podczas wywoływania statycznej metody wewnątrz klasy, funkcja wywołująca musi być metodą klasy, więc dodanie @classmethod do definicji metody może rozwiązać problem.

The definitive guide on how to use static, class or abstract methods in Python

 1
Author: whimian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-28 08:36:05