Вызов класса staticmethod внутри тела класса?

Когда я пытаюсь использовать статический метод изнутри тела класса и определяю статический метод, используя встроенную функцию staticmethod в качестве декоратора, например:

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

Я получаю следующую ошибку:

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

Я понимаю, почему это происходит (привязка дескриптора) и может обойти его, вручную преобразовывая _stat_func() в статический метод после его последнего использования, например:

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

Итак, мой вопрос:

Есть ли лучшие, как в чистом или более "питоническом", способы достижения этого?

Ответ 1

Объекты staticmethod очевидно, имеют атрибут __func__ хранящий исходную необработанную функцию (имеет смысл, что они должны были). Так что это будет работать:

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

Помимо этого, хотя я подозревал, что у объекта staticmethod есть своего рода атрибут, хранящий исходную функцию, я понятия не имел о специфике. В духе обучения кого-то ловить рыбу, а не давать ему рыбу, это то, что я сделал, чтобы исследовать и выяснить это (C & P из моей сессии Python):

>>> 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>

Подобные виды копания в интерактивном сеансе (dir очень полезен) часто могут решить такие вопросы очень быстро.

Ответ 2

Так я предпочитаю:

class Klass(object):

    @staticmethod
    def stat_func():
        return 42

    _ANS = stat_func.__func__()

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

Я предпочитаю это решение Klass.stat_func из-за принципа причине, почему в Python 3 есть новый super():

Но я согласен с другими, обычно лучшим выбором является определение функции уровня модуля.

Например, с функцией @staticmethod рекурсия может выглядеть не очень хорошо (вам нужно будет разбить принцип DRY, вызвав Klass.stat_func внутри Klass.stat_func). Это потому, что у вас нет ссылки на self внутри статического метода. С функцией уровня модуля все будет выглядеть нормально.

Ответ 3

Это связано с тем, что staticmethod является дескриптором и требует, чтобы атрибут класса на уровне выполнял протокол дескриптора и получал истинное вызываемое.

Из исходного кода:

Он может быть вызван либо в классе (например, C.f()), либо в экземпляре (например, C().f()); экземпляр игнорируется, за исключением его класса.

Но не непосредственно изнутри класса, пока он определяется.

Но, как заметил один из комментаторов, это вовсе не "питонический" дизайн. Вместо этого используйте функцию уровня модуля.

Ответ 4

Как насчет этого решения? Он не полагается на знание реализации @staticmethod decorator. Внутренний класс StaticMethod играет роль контейнера статических функций инициализации.

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

Ответ 5

Как вводить атрибут класса после определения класса?

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

В соответствии с блоком ниже, при вызове статического метода в классе функция вызывающего абонента должна быть методом класса, поэтому добавление @classmethod в определение метода может решить проблему.

Окончательное руководство по использованию статических, классных или абстрактных методов в Python