Аргументы декоратора класса Python
Я пытаюсь передать необязательные аргументы моему декоратору класса в python.
Ниже кода, который я сейчас имею:
class Cache(object):
def __init__(self, function, max_hits=10, timeout=5):
self.function = function
self.max_hits = max_hits
self.timeout = timeout
self.cache = {}
def __call__(self, *args):
# Here the code returning the correct thing.
@Cache
def double(x):
return x * 2
@Cache(max_hits=100, timeout=50)
def double(x):
return x * 2
Второй декоратор с аргументами для перезаписывания по умолчанию (max_hits=10, timeout=5
в моей функции __init__
) не работает, и я получил исключение TypeError: __init__() takes at least 2 arguments (3 given)
. Я пробовал много решений и читал статьи об этом, но здесь я все еще не могу заставить его работать.
Любая идея решить это? Спасибо!
Ответы
Ответ 1
@Cache(max_hits=100, timeout=50)
вызывает __init__(max_hits=100, timeout=50)
, поэтому вы не удовлетворяете аргументу function
.
Вы можете реализовать свой декоратор с помощью метода обертки, который обнаружил, присутствует ли функция. Если он найдет функцию, он может вернуть объект Cache. В противном случае он может вернуть функцию-обертку, которая будет использоваться в качестве декоратора.
class _Cache(object):
def __init__(self, function, max_hits=10, timeout=5):
self.function = function
self.max_hits = max_hits
self.timeout = timeout
self.cache = {}
def __call__(self, *args):
# Here the code returning the correct thing.
# wrap _Cache to allow for deferred calling
def Cache(function=None, max_hits=10, timeout=5):
if function:
return _Cache(function)
else:
def wrapper(function):
return _Cache(function, max_hits, timeout)
return wrapper
@Cache
def double(x):
return x * 2
@Cache(max_hits=100, timeout=50)
def double(x):
return x * 2
Ответ 2
@Cache
def double(...):
...
эквивалентно
def double(...):
...
double=Cache(double)
В то время как
@Cache(max_hits=100, timeout=50)
def double(...):
...
эквивалентно
def double(...):
...
double = Cache(max_hits=100, timeout=50)(double)
Cache(max_hits=100, timeout=50)(double)
имеет очень разную семантику, чем Cache(double)
.
Неразумно пытаться сделать Cache
обрабатывать оба варианта использования.
Вместо этого вы можете использовать декоратор factory, который может принимать необязательные аргументы max_hits
и timeout
и возвращает декоратор:
class Cache(object):
def __init__(self, function, max_hits=10, timeout=5):
self.function = function
self.max_hits = max_hits
self.timeout = timeout
self.cache = {}
def __call__(self, *args):
# Here the code returning the correct thing.
def cache_hits(max_hits=10, timeout=5):
def _cache(function):
return Cache(function,max_hits,timeout)
return _cache
@cache_hits()
def double(x):
return x * 2
@cache_hits(max_hits=100, timeout=50)
def double(x):
return x * 2
PS. Если класс Cache
не имеет других методов помимо __init__
и __call__
, вы, вероятно, можете переместить весь код внутри функции _cache
и полностью исключить Cache
.
Ответ 3
Я многое узнал из этого вопроса, спасибо всем. Разве это не ответ, чтобы положить пустые скобки на первый @Cache
? Затем вы можете переместить параметр function
в __call__
.
class Cache(object):
def __init__(self, max_hits=10, timeout=5):
self.max_hits = max_hits
self.timeout = timeout
self.cache = {}
def __call__(self, function, *args):
# Here the code returning the correct thing.
@Cache()
def double(x):
return x * 2
@Cache(max_hits=100, timeout=50)
def double(x):
return x * 2
Хотя я считаю, что этот подход более простой и более краткий:
def cache(max_hits=10, timeout=5):
def caching_decorator(fn):
def decorated_fn(*args ,**kwargs):
# Here the code returning the correct thing.
return decorated_fn
return decorator
Если вы забудете круглые скобки при использовании декоратора, к сожалению, вы до сих пор не получаете ошибку до времени выполнения, так как внешним параметрам декоратора передается функция, которую вы пытаетесь украсить. Затем во время выполнения внутренний декоратор жалуется:
TypeError: caching_decorator() принимает ровно 1 аргумент (задано 0).
Однако вы можете поймать это, если знаете, что параметры вашего декоратора никогда не будут вызываемыми:
def cache(max_hits=10, timeout=5):
assert not callable(max_hits), "@cache passed a callable - did you forget to parenthesize?"
def caching_decorator(fn):
def decorated_fn(*args ,**kwargs):
# Here the code returning the correct thing.
return decorated_fn
return decorator
Если вы сейчас попробуете:
@cache
def some_method()
pass
Вы получаете декларацию AssertionError
.
В общем касании я наткнулся на это сообщение, ища декораторов, которые украшают классы, а не классы, которые украшают. В случае, если кто-то еще делает это, этот вопрос полезен.