Python 闭包到装饰器由浅入深解析

我想大家都知道装饰器的意义,拓展函数的新功能。装饰的对象是函数,本身也是函数和闭包的结合。先给出实例:计算函数运行的时间(通过装饰器来实现这样我不必在乎实现计算什么样的函数的运行时间,而是专注拓展计算时间这样的功能)

from time import time

def clock(func):
	print('为%s函数注册clock装饰器!' % func)
	def wrapper():
		t = time()
		func()
		print('%s运行时间:%sS' % (func.__name__, time()-t))
	return wrapper

@clock
def foo():
	for i in range(8):
		print(i)

if __name__ == '__main__':
	foo()



>>>为<function foo at 0x000001E73F1F7488>函数注册clock装饰器!
0
1
2
3
4
5
6
7
foo运行时间:0.0003437995910644531S
[Finished in 0.3s]

同理我们可以实现任何foo()函数,具体功能由foo()实现,计算时间由clock实现。

第一个有意思的地方:

if __name__ == '__main__':
	pass

>>>为<function foo at 0x0000022B51E17488>函数注册clock装饰器!
[Finished in 0.3s]

main部分不做任何处理装饰器编译时就被注册(clock被注册的时间和@clock同时发生,大家可以直接from import来观察--参考流畅的python第7章)

第二认识和理解装饰器必须经历闭包(closure)

那么我们开始通过闭包自己手撸一个clock装饰器:

from time import time

def clock(func):
	print('为%s函数注册clock装饰器!' % func)
	def wrapper():
		t = time()
		func()
		print('%s运行时间:%sS' % (func.__name__, time()-t))
	return wrapper

def foo():
	for i in range(1000):
		print(i)

if __name__ == '__main__':
	_clock = clock(foo)
	_clock()

>>>为<function foo at 0x000001D869F47378>函数注册clock装饰器!
...
997
998
999
foo运行时间:0.010381460189819336S
[Finished in 0.3s]

很简单不是么?闭包其实也就是装饰器实现的具体依据,我们只是用@clock这个语法糖实现了main主体部分的功能并用foo覆盖原来的我们可以看下foo装饰后到底时什么

from time import time

def clock(func):
	print('为%s函数注册clock装饰器!' % func)
	def wrapper():
		t = time()
		func()
		print('%s运行时间:%sS' % (func.__name__, time()-t))
	return wrapper
@clock
def foo():
	for i in range(1000):
		print(i)

if __name__ == '__main__':
	print(foo)

>>>为<function foo at 0x000002BD6A3B7488>函数注册clock装饰器!
<function clock.<locals>.wrapper at 0x000002BD6A3B7378>
[Finished in 0.2s]

这样是不是更能说明装饰器就是闭包呢?

闭包拓展自由变量等其他的特点可以参考《流畅的python》第七章大佬的表达比我要巧妙地多

闭包地传参foo(x)的实现,既然闭包就是装饰器,返回的是wrapper那么参数就得在wrapper中传递<因为foo=clock.wrapper>

那么解决下传参的问题吧:

from time import time

def clock(func):
	print('为%s函数注册clock装饰器!' % func)
	def wrapper(*arg):
		t = time()
		func(*arg)
		print('%s运行时间:%sS' % (func.__name__, time()-t))
	return wrapper
@clock
def foo(x):
	for i in range(x):
		print(i)

if __name__ == '__main__':
	foo(100)

至于clock的传参就要再多一层闭包。此处给出不做解释大家可以照上面的思路理解。

from time import time

def clock(great):
	print(great+'你好')
	def _wrapper(func):
		def wrapper(*arg):
			t = time()
			func(*arg)
			print('%s运行时间:%sS' % (func.__name__, time()-t))
		return wrapper
	return _wrapper

@clock('foo')
def foo(x):
	for i in range(x):
		print(i)

if __name__ == '__main__':
	foo(100)

【clock('foo')._wrapper.wrapper = foo--手撸clock('foo')(foo)(100)】

将闭包和装饰器一起理解会有新的发现,而且闭包可以实现一些优秀的技巧(自由变量的灵活使用)。此处致敬流畅的python的作者,并向大家安利下。

博文写的不多。不到之处望指正。谢谢

猜你喜欢

转载自blog.csdn.net/qq_35189715/article/details/83037706