1. 程式人生 > >python3之裝飾器實現 上篇

python3之裝飾器實現 上篇

裝飾器是python中一個強大的功能,利用裝飾器可以在不必改動原有函式的前提下增加一些功能,可以被用於日誌記錄、許可權驗證、除錯測試、事務處理等。裝飾器是閉包的一種形式,閉包是python函式程式設計的高階用法,學習閉包之前讓我們先了解python函式的概念理解和高階用法。本篇文章會結合閉包的形式和使用中的一些注意點分為上下兩篇。

概念理解

<1>變數可以指向函式,函式物件和執行函式的定義

def add(x,y):
	return x+y

fn = add
fv = add(3,3)
print(add)
print(fn)
print(fv)
print(add(2,3))	
print(fn(4,5))

執行結果:

<function add at 0x0000019ED6503840>
<function add at 0x0000019ED6503840>
6
5
9

解析:程式碼<1>中定義了一個add()函式,列印add可以輸出函式物件,列印add()輸出函式執行後的結果。同時變數fn可以指向函式add,通過變數fn()可以呼叫add()函式。變數fv指向了執行函式add(3,3),打印出函式執行後的結果。

python函式高階用法

<1>函式可以作為引數進行傳遞

def add(x, y, f):
    return f(x) + f(y)
print(add(-5, 6, abs))

執行結果

11

<2>函式作為結果值返回

def sum(x,y):
	def cal():
		return x+y
	return cal

f = sum(3,5)
print(f())

執行結果:

8

解析:程式碼<2>中f呼叫執行sum()函式,函式cal物件作為返回值,此時x+y並沒有執行,f()呼叫執行cal()函式,列印計算x+y結果。f呼叫執行sum()函式,同時接收sum()函式的返回值,此時 f=cal

為了說明閉包和巢狀函式的不同,讓我們先看下兩段程式碼,同時引入函式變數作用域的概念。 <1>

x = 0 # 模組級別定義的全域性變數
def outer():
	x = 1 # 巢狀中父級函式的區域性作用域變數
	def inner():
		x = 2 # 函式中定義的變數
		print("local: x=",x)
	inner()
	print("enclosing: x=",x)


outer()
print("global: x=",x)

執行結果:

local: x= 2
enclosing: x= 1
global: x= 0

解析:程式碼<1>即平常使用的函式巢狀,python允許出現同名變數,具有相同命名標識的變數在同一函式體中或具有函式巢狀關係,則不同作用域的變數各自代表不同的物件。 如果函式巢狀使用時,內部的inner函式需要傳入引數該如何實現,我們來看接下來的程式碼。 <2>

def outer(x):
    def inner(y):
        print(x + y)
    return inner

f = outer(5)
f(10)

解析:程式碼<2>中函式inner作為結果返回。若呼叫某函式時,該函式將其內部定義的函式作為返回值,則所返回的函式稱為閉包。f呼叫執行outer()函式,同時接收outer()函式的返回值,此時 f=inner,f(10)列印輸出15(5+10=15)

歸納:函式中,誰呼叫執行函式,同時接收執行函式的返回值。閉包的特點在於從外部能向內部的函式傳遞引數。

利用閉包實現裝飾器 <1>示例程式碼

def add(x,y):
    return x+y

print(add(2,3))

執行結果:

5

如果此時我們需要在函式執行前打印出函式的引數,又不想改變原來的程式碼,應該怎麼做呢?可以使用裝飾器去實現。 <2>

def decorator(f):
    def wrapper(x,y):
        print("引數1為:%s,引數2為:%s"%(x,y))
        return f(x,y)
    return wrapper
@decorator
def add(x,y):
    return x+y

print(add(2,3))

執行結果:

5

這種語法結構就是裝飾器,遵循了程式碼編寫的開放封閉原則,即已經實現的功能程式碼不允許被修改,但可以被擴充套件。雖然在這個原則是用的面向物件開發,但是也適用於函數語言程式設計。 關於閉包使用中的注意事項,以及裝飾器的內部原理和高階用法,我們在python之裝飾器下篇介紹。