Когда я пытаюсь использовать статический метод из тела класса, и определяю статический метод, используя встроенную 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()
метод static после его последнего использования, например, так:
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
Итак, мой вопрос:
Есть ли лучшие, как в более чистых или более «Pythonic», способы сделать это?
python
decorator
static-methods
Мартино
источник
источник
staticmethod
вообще. Они обычно более полезны как функции уровня модуля, и в этом случае ваша проблема не является проблемой.classmethod
с другой стороны ...Ответы:
staticmethod
у объектов, по-видимому, есть__func__
атрибут, хранящий исходную необработанную функцию (имеет смысл, что они должны были). Так что это будет работать:Помимо этого, хотя я подозревал, что у объекта staticmethod есть какой-то атрибут, хранящий исходную функцию, я понятия не имел о специфике. В духе обучения кого-то ловить рыбу, а не давать ему рыбу, это то, что я сделал, чтобы исследовать и выяснить это (C & P из моей сессии Python):
Подобные виды копания в интерактивном сеансе (
dir
очень полезно) часто могут решить такие вопросы очень быстро.источник
__func__
это просто другое названиеim_func
и было добавлено в Py 2.6 для прямой совместимости с Python 3.__func__
Атрибут статического метода возвращает вам ссылку на исходную функцию, точно так же, как если бы вы никогда не использовалиstaticmethod
декоратор. Поэтому, если вашей функции требуются аргументы, вам придется передавать их при вызове__func__
. Сообщение об ошибке у вас звучит так, будто вы не дали ему никаких аргументов. Если быstat_func
в примере этого поста было два аргумента, вы бы использовали_ANS = stat_func.__func__(arg1, arg2)
stat_func
сама такая переменная). Вы имели в виду, что не можете использовать атрибуты экземпляра определяемого вами класса? Это правда, но неудивительно; мы не имеем экземпляр класса, так как мы до сих пор определяющие класс! Но, во всяком случае, я просто имел в виду, что они заменяют любые аргументы, которые вы хотели бы передать; Вы могли бы использовать литералы там.Это способ, которым я предпочитаю:
Я предпочитаю это решение
Klass.stat_func
из-за принципа СУХОЙ . Напоминает мне причину появления новогоsuper()
в Python 3 :)Но я согласен с другими, обычно лучшим выбором является определение функции уровня модуля.
Например, с
@staticmethod
функцией рекурсия может выглядеть не очень хорошо (вам нужно нарушить принцип DRY, вызываяKlass.stat_func
внутриKlass.stat_func
). Это потому, что у вас нет ссылки наself
внутренний статический метод. С функцией уровня модуля все будет выглядеть хорошо.источник
self.__class__.stat_func()
в обычных методах имеет преимущества (СУХОЙ и все такое) по сравнению с использованиемKlass.stat_func()
, это не было темой моего вопроса - на самом деле я избегал использовать первое, чтобы не затуманить проблему несоответствия.self.__class__
лучше, потому что если подкласс переопределяетstat_func
, тоSubclass.method
вызовет подклассstat_func
. Но, если честно, в такой ситуации гораздо лучше использовать реальный метод, а не статический.Как насчет введения атрибута класса после определения класса?
источник
Это происходит из-за того, что staticmethod является дескриптором и требует выборки атрибутов на уровне класса, чтобы использовать протокол дескриптора и получить истинный вызываемый объект.
Из исходного кода:
Но не прямо из класса, пока он определяется.
Но, как заметил один комментатор, на самом деле это не «Pythonic» дизайн. Просто используйте вместо этого функцию уровня модуля.
источник
Как насчет этого решения? Он не зависит от знаний о
@staticmethod
реализации декоратора. Внутренний класс StaticMethod играет роль контейнера статических функций инициализации.источник
__func__
потому что это теперь официально задокументировано (see section Другие языковые изменения в Что нового в Python 2.7 и его ссылка на Выпуск 5982 ). Ваше решение еще более переносимо, поскольку, вероятно, оно также будет работать в версиях Python до версии 2.6 (когда__func__
впервые было введено как синонимim_func
).