На этот ответ уже давно дан ответ, но я подумала, что поделюсь своим классом Decorator, который делает написание новых декораторов простым и компактным.
from abc import ABCMeta, abstractclassmethod
class Decorator(metaclass=ABCMeta):
""" Acts as a base class for all decorators """
def __init__(self):
self.method = None
def __call__(self, method):
self.method = method
return self.call
@abstractclassmethod
def call(self, *args, **kwargs):
return self.method(*args, **kwargs)
С одной стороны, я думаю, что это делает поведение декораторов очень ясным, но также позволяет очень просто определять новые декораторы. Для приведенного выше примера вы можете решить его следующим образом:
class MakeBold(Decorator):
def call():
return "<b>" + self.method() + "</b>"
class MakeItalic(Decorator):
def call():
return "<i>" + self.method() + "</i>"
@MakeBold()
@MakeItalic()
def say():
return "Hello"
Вы также можете использовать его для выполнения более сложных задач, например, для декоратора, который автоматически делает рекурсивное применение функции ко всем аргументам в итераторе:
class ApplyRecursive(Decorator):
def __init__(self, *types):
super().__init__()
if not len(types):
types = (dict, list, tuple, set)
self._types = types
def call(self, arg):
if dict in self._types and isinstance(arg, dict):
return {key: self.call(value) for key, value in arg.items()}
if set in self._types and isinstance(arg, set):
return set(self.call(value) for value in arg)
if tuple in self._types and isinstance(arg, tuple):
return tuple(self.call(value) for value in arg)
if list in self._types and isinstance(arg, list):
return list(self.call(value) for value in arg)
return self.method(arg)
@ApplyRecursive(tuple, set, dict)
def double(arg):
return 2*arg
print(double(1))
print(double({'a': 1, 'b': 2}))
print(double({1, 2, 3}))
print(double((1, 2, 3, 4)))
print(double([1, 2, 3, 4, 5]))
Какие отпечатки:
2
{'a': 2, 'b': 4}
{2, 4, 6}
(2, 4, 6, 8)
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
Обратите внимание, что в этом примере не был указан тип list
в экземпляре декоратора, поэтому в последнем операторе печати метод применяется к самому списку, а не к элементам списка.