Все, что нам действительно нужно сделать, - это изменить поведение декоратора так, чтобы оно было «гигиеническим», то есть сохраняющим атрибуты.
#!/usr/bin/python3
def hygienic(decorator):
def new_decorator(original):
wrapped = decorator(original)
wrapped.__name__ = original.__name__
wrapped.__doc__ = original.__doc__
wrapped.__module__ = original.__module__
return wrapped
return new_decorator
Это ВСЕ, что вам нужно. В общем. Он не сохраняет подпись, но если вы действительно этого хотите, вы можете использовать библиотеку для этого. Я также переписал код памятки, чтобы он работал и с аргументами ключевых слов. Также была ошибка, из-за которой невозможность конвертировать его в хешируемый кортеж приводила к тому, что он не работал в 100% случаев.
Демонстрация переписанного memoized
декоратора с @hygienic
изменением его поведения. memoized
теперь является функцией, которая оборачивает исходный класс, хотя вы можете (как и в другом ответе) вместо этого написать класс обтекания или, что еще лучше, что-то, что определяет, является ли он классом, и, если да, обертывает метод __init__
.
@hygienic
class memoized:
def __init__(self, func):
self.func = func
self.cache = {}
def __call__(self, *args, **kw):
try:
key = (tuple(args), frozenset(kw.items()))
if not key in self.cache:
self.cache[key] = self.func(*args,**kw)
return self.cache[key]
except TypeError:
# uncacheable -- for instance, passing a list as an argument.
# Better to not cache than to blow up entirely.
return self.func(*args,**kw)
В действии:
@memoized
def f(a, b=5, *args, keyword=10):
"""Intact docstring!"""
print('f was called!')
return {'a':a, 'b':b, 'args':args, 'keyword':10}
x=f(0)
#OUTPUT: f was called!
print(x)
#OUTPUT: {'a': 0, 'b': 5, 'keyword': 10, 'args': ()}
y=f(0)
#NO OUTPUT - MEANS MEMOIZATION IS WORKING
print(y)
#OUTPUT: {'a': 0, 'b': 5, 'keyword': 10, 'args': ()}
print(f.__name__)
#OUTPUT: 'f'
print(f.__doc__)
#OUTPUT: 'Intact docstring!'