На этом шаге мы рассмотрим решение этой задачи.
Вы хотите применить декоратор к методу класса или статическому методу.
Применение декораторов к методам класса и статическим методам выполняется обычным способом, но нужно убедиться, что ваши декораторы применяются к методам перед @classmethod и @staticmethod. Например:
>>> import time >>> from functools import wraps >>> # Простой декоратор >>> def timethis(func): @wraps(func) def wrapper(*args, **kwargs): start = time.time() r = func(*args, **kwargs) end = time.time() print(end - start) return r return wrapper >>> # Класс, иллюстрирующий применение декоратора к различным типам методов >>> class Spam: @timethis def instance_method(self, n): print(self, n) while n > 0: n -= 1 @classmethod @timethis def class_method(cls, n): print(cls, n) while n > 0: n -= 1 @staticmethod @timethis def static_method(n): print(n) while n > 0: n -= 1 >>>
Получившиеся метод класса и статический метод должны работать как обычно, но с добавлением дополнительной функциональности:
>>> s = Spam() >>> s.instance_method(10000000) <__main__.Spam object at 0x00000107349F8400> 10000000 1.2668874263763428 >>> Spam.class_method(10000000) <class '__main__.Spam'> 10000000 1.2826385498046875 >>> Spam.static_method(10000000) 10000000 1.2948384284973145 >>>
Если вы перепутаете порядок применения декораторов, то получите ошибку. Например, если вы сделаете так:
class Spam: . . . @timethis @staticmethod def static_method(n): print(n) while n > 0: n -= 1
>>> Spam.static_method(1000000)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "timethis.py", line 6, in wrapper start = time.time()
TypeError: 'staticmethod' object is not callable
>>>
Есть ситуация, где этот рецепт имеет чрезвычайную важность: при определении классов и статических методов в абстрактных базовых классах, как описано на 141 шаге. Например, если вы хотите определить метод абстрактного класса, то можете использовать такой код:
from abc import ABCMeta, abstractmethod class A(metaclass=ABCMeta): @classmethod @abstractmethod def method(cls): pass
В этом фрагменте порядок @classmethod и @abstractmethod имеет значение. Если вы поменяете местами эти декораторы, все поломается.
На следующем шаге мы рассмотрим написание декораторов, которые добавляют аргументы обернутым функциям.