1. 程式人生 > >Python 閉包到裝飾器由淺入深解析

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的作者,並向大家安利下。

博文寫的不多。不到之處望指正。謝謝