Python Декоратор функции с передачей аргумента.

Сами по себе декораторы весьма просты для восприятия, а благодаря минималистичному синтаксису питона исопльзовать их краней просто. Однако есть ряд нюансов, читая книгу Марка Лутса (4-е издание) мне понравились примеры в конце книги, а также разбор нюансов (такиих как ошибка при попытке декорирования класса (с вызовом call) другого класса и обход этой ошибки с использованием дескриптора, но об этом чуть позже).
Что для меня явилялось новинкой это передача аргументов в декоратор, возможно потому что я не сталкивался с необходимостью в этом, и всеже весьма лаконичное ршение. Как пример позволю себе взять функцию предложенную в выше указанной книге, это своего рода бенчмарк для измерения времени выполнения функции.
from time import time as now
def code_timer(label=''):
def decorator(func):
def onCall(*args):
start_time = now()
val = func(*args)
print("[%s] ==> %s" % (label, now() - start_time))
return val
return onCall
return decorator
Данная фунция возвращает декоратор, который в свою очередь, возврщает нашу функцию. Стоит отметить что видимость внутри последней в иерархии функции переменной label достигается за счет поиска в объемлящей функции. Внутри onCall засекается время, вызывается оригинальная функция, затем вычисляется разица во времени и возвращается результат. Данный декоратор (как и все декораторы-функции) могут применяться как к другим функциям, так и к классам с методом call.
@code_timer('sqr')
def fctrl(n):
if n == 1:
return 1
else:
return n * fctrl(n - 1)
@code_timer('class_sqr')
class sqrr:
def __init__(self, n):
self.n = n
def __call__(self):
return self.n ** 1024
if __name__ == "__main__":
print(fctrl(100))
a = sqrr(100)
a()
Результатом данных функций будет следующий лог:
[sqr] ==> 9.53674316406e-07
[sqr] ==> 0.000109910964966
...
[sqr] ==> 0.000827074050903
[class_sqr] ==> 1.71661376953e-05
traceMe = False
def trace(*args):
if traceMe: print('[' + ' '.joinx(map(str, args)) + ']')
def private(*privates):
def onDeorator(aClass):
class onInstance:
def __init__(self, *args, **kwargs):
self.wrapped = aClass(*args, **kwargs)
def __getattr__(self, attr):
trace('get:', attr)
if attr in privates:
raise TypeError("This is private attribute: %s" % attr)
else:
return getattr(self.wrapped, attr)
def __setattr__(self, attr, value):
trace('set:', attr, value)
if attr == 'wrapped':
self.__dict__[attr] = value
elif attr in privates:
raise TypeError("This is private attribute: %s" % attr)
else:
setattr(self.wrapped, attr, value)
return onInstance
return onDeorator
@private('data')
class T:
def __init__(self, kek, data):
self.data = data
self.kek = kek
def p(self,):
print(self.data)
print(self.kek)
a = T(1, 12)
a.p()
a.data = 1