Вызов класса 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