Python декоратор как статический метод
Я пытаюсь написать класс Python, который использует функцию декоратора, которая нуждается в информации о состоянии экземпляра. Это работает как задумано, но если я явно сделаю декоратор staticmetod, я получу следующую ошибку:
Traceback (most recent call last):
File "tford.py", line 1, in <module>
class TFord(object):
File "tford.py", line 14, in TFord
@ensure_black
TypeError: 'staticmethod' object is not callable
Зачем?
Вот код:
class TFord(object):
def __init__(self, color):
self.color = color
@staticmethod
def ensure_black(func):
def _aux(self, *args, **kwargs):
if self.color == 'black':
return func(*args, **kwargs)
else:
return None
return _aux
@ensure_black
def get():
return 'Here is your shiny new T-Ford'
if __name__ == '__main__':
ford_red = TFord('red')
ford_black = TFord('black')
print ford_red.get()
print ford_black.get()
И если я просто уберу строку @staticmethod
, все работает, но я не понимаю почему. Разве это не нужно self
в качестве первого аргумента?
3 ответа
Это не как staticmethod
должен быть использован. staticmethod
объекты - это дескрипторы, которые возвращают обернутый объект, поэтому они работают только при доступе как classname.staticmethodname
, пример
class A(object):
@staticmethod
def f():
pass
print A.f
print A.__dict__["f"]
печать
<function f at 0x8af45dc>
<staticmethod object at 0x8aa6a94>
Внутри сферы A
Вы всегда получите последний объект, который не вызывается.
Я бы настоятельно рекомендовал перенести декоратор в область видимости модуля - он, кажется, не принадлежит классу. Если вы хотите держать его в классе, не делайте его staticmethod
, а скорее просто del
это в конце тела класса - в этом случае он не предназначен для использования вне класса.
Классы Python создаются во время выполнения после оценки содержимого объявления класса. Класс оценивается путем присвоения всех объявленных переменных и функций специальному словарю и использования этого словаря для вызова type.__new__
(см. настройку создания класса).
Так,
class A(B):
c = 1
эквивалентно:
A = type.__new__("A", (B,), {"c": 1})
Когда вы аннотируете метод с помощью @staticmethod, после создания класса создается особая магия type.__new__
, Внутри области объявления класса функция @staticmethod является просто экземпляром объекта staticmethod, который вы не можете вызвать. Декоратор, вероятно, должен быть объявлен выше определения класса в том же модуле ИЛИ в отдельном модуле "decorate" (зависит от того, сколько у вас декораторов). В общем случае декораторы должны быть объявлены вне класса. Одним заметным исключением является класс свойств (см. Свойства). В вашем случае наличие декоратора внутри объявления класса может иметь смысл, если у вас есть что-то вроде цветового класса:
class Color(object):
def ___init__(self, color):
self.color = color
def ensure_same_color(f):
...
black = Color("black")
class TFord(object):
def __init__(self, color):
self.color = color
@black.ensure_same_color
def get():
return 'Here is your shiny new T-Ford'
Сегодня я хотел, чтобы декоратор статических методов использовался только внутри моего класса.
Проблема в том, что статический метод, который пытается использовать в качестве декоратора, на самом деле является объектом staticmethod и не вызывается.
Решение: объект staticmethod имеет метод__get__
который принимает любой аргумент и возвращает реальный метод: документация Python Python 3.5 и выше:
class StaticMethod(object):
"Emulate PyStaticMethod_Type() in Objects/funcobject.c"
def __init__(self, f):
self.f = f
def __get__(self, obj, objtype=None):
return self.f
Минимальное решение, с которым я пришел:
class A():
def __init__(self):
self.n = 2
@staticmethod
def _addtoglobaltime(func):
from functools import wraps
@wraps(func)
def wrapper(*args, **kwargs):
self = args[0]
response = func(*args, **kwargs)
return self.n, response
return wrapper
@_addtoglobaltime.__get__('this can be anything')
def get(self):
return self.n**2
if __name__ == '__main__':
a = A()
print(a.get())
Напечатает (2, 4)
ensure_black
возвращает _aux
метод, который не украшен @staticmethod
Вы можете вернуть нестатический метод в static_method