29 Ноя


2017

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

Сами по себе декораторы весьма просты для восприятия, а благодаря минималистичному синтаксису питона исопльзовать их краней просто. Однако есть ряд нюансов, читая книгу Марка Лутса (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

Еще 1 интересный пример из книги который я бы хотел запомнить это создание приватных атрибутов, нативно в питоне приватности как таковой нет, она достигается условным обозначением приватных переменных __var, _var, однако ничего не мешает их изменить в будущем. Данный декоратор мб полезен например при создании фреймворка или если необходимо точно убедиться что внутренние атрибуты метода не будут изменены.

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

 

фичи