Декораторы классов для методов в классах
Как работают декораторы классов для методов в классах? Вот пример того, что я сделал с помощью некоторых экспериментов:
from functools import wraps
class PrintLog(object):
def __call__(self, func):
@wraps(func)
def wrapped(*args):
print('I am a log')
return func(*args)
return wrapped
class foo(object):
def __init__(self, rs: str) -> None:
self.ter = rs
@PrintLog()
def baz(self) -> None:
print('inside baz')
bar = foo('2')
print('running bar.baz()')
bar.baz()
И это отлично работает. Однако у меня сложилось впечатление, что декораторов не нужно вызывать с
()
, но когда я убираю скобки из
@PrintLog()
, я получаю эту ошибку:
def baz(self) -> None:
TypeError: PrintLog() takes no arguments
Я что-то упускаю/не понимаю? Я также пытался передать одноразовый аргумент с помощью
__init__()
, и это работает.
class PrintLog(object):
def __init__(self, useless):
print(useless)
def __call__(self, func):
@wraps(func)
def wrapped(*args):
print('I am a log')
return func(*args)
return wrapped
class foo(object):
def __init__(self, rs: str) -> None:
self.ter = rs
@PrintLog("useless arg that I'm passing to __init__")
def baz(self) -> None:
print('inside baz')
Опять же, это работает, но я не хочу передавать какие-либо аргументы декоратору.
tl;dr: этот вопрос в python 3.x.
Помощь приветствуется!
2 ответа
Декораторы класса принимают функцию в качестве субъекта внутри
__init__
метод (отсюда и сообщение журнала), поэтому ваш код декоратора должен выглядеть так:
class PrintLog(object):
def __init__(self, function):
self.function = function
def __call__(self):
@wraps(self.function)
def wrapped(*args):
print('I am a log')
return self.function(*args)
return wrapped
Извините, если это не работает, я отвечаю на своем мобильном устройстве.
РЕДАКТИРОВАТЬ:
Итак, это, вероятно, не то, что вы хотите, но вот способ сделать это:
from functools import update_wrapper, partial, wraps
class PrintLog(object):
def __init__(self, func):
update_wrapper(self, func)
self.func = func
def __get__(self, obj, objtype):
"""Support instance methods."""
return partial(self.__call__, obj)
def __call__(self, obj, *args, **kwargs):
@wraps(self.func)
def wrapped(*args):
print('I am a log')
return self.func(*args)
return wrapped(obj, *args)
class foo(object):
def __init__(self, rs: str) -> None:
self.ter = rs
@PrintLog
def baz(self) -> None:
print('inside baz')
bar = foo('2')
print('running bar.baz()')
bar.baz()
Декоратор должен иметь
__get__
метод определен, потому что вы применяете декоратор к методу экземпляра. Как дескриптор будет иметь контекст
foo
пример?
Ссылка: Декорирование методов класса Python — как передать экземпляр декоратору?
Вы упускаете большую картину.
@decorator
def foo(...):
function_definition
почти идентичен (за исключением некоторой внутренней деформации)
temp = foo
foo = decorator(temp)
Неважно, что такое декоратор, если он может действовать как функция.
Ваш пример эквивалентен:
baz = PrintLog("useless thing")(<saved defn of baz>)
Поскольку это класс,
PrintLog(...)
создает экземпляр
PrintLog
. Этот экземпляр имеет
__call__
метод, поэтому он может действовать как функция.
Некоторые декораторы предназначены для приема аргументов. Некоторые декораторы не принимают аргументы. Некоторые, как
@lru_cache
, являются кусочками магии Python, которые смотрят, является ли «аргумент» функцией (поэтому декоратор используется напрямую) или числом/None, так что он возвращает функцию, которая затем становится декоратором.