Python閉包和裝飾器

2021-10-12 12:47:38 字數 4164 閱讀 4682

在巢狀函式中引用了自由變數的函式。這個自由變數就是外層巢狀函式中的變數(非全域性變數)
1. 必須是巢狀函式

2. 內層巢狀函式必須引用了外層巢狀函式的變數

3. 外層巢狀函式的返回值是內層巢狀函式

巢狀函式的內層函式可以使用外層函式的變數,即使外層函式返回了,或者被刪除了。內層函式依然可以使用外層函式的那個變數。
# encoding:utf-8

# 判斷閉包的方法closure,若果是none就不是閉包,如果有cell元素就是閉包

deffunc1()

: num1 =

10def

func2()

:pass

print

("func2:"

, func2.__closure__)

return func2

print

(func1())

# output: func2: none, 因為內層巢狀函式必須引用了外層巢狀函式的變數

deffunc1()

: num1 =

10def

func2()

:print

(num1)

return num1

print

("func2:"

, func2.__closure__)

return func2

func1(

)# output func2: (,)

# 閉包,fn使用了變數y並且外層函式make_power的返回值是fn(內層巢狀函式)

# 這個閉包可以實現x的y次方

defmake_power

(y):

# 自由變數就是y,當函式make_power返回後,fn依舊可以使用y的值

deffn

(x):

return x * y

return fn

res = make_power(10)

print

(res(2)

)#output:20

簡單來說,裝飾器本質上就是閉包函式。一般就是傳入乙個函式或類,在不修改原來的函式以及其呼叫方式的前提下為原來的函式增加新的功能。通常的做法是,在裝飾器函式的內層函式中呼叫被裝飾的函式然後在被裝飾的函式呼叫前面或者後面加上我們要執行的**,達到擴充套件其功能的目的。比較常用的場景就是日誌插入,事務處理。其實裝飾器函式本質上就是函式名和函式位址的重新繫結。被裝飾的函式雖然看起來和原來的函式名字是一樣的,但是其在記憶體中已經修改了繫結關係,它已經繫結到我們裝飾器函式的內層函式。
乙個初始的函式

如果想要記錄函式的執行時間,最原始的做法是入侵到原來的函式中進行修改

import time

# 計算乙個函式的執行的時間,最暴力的方法

deffunc()

: starttime = time.time(

)print

("hello"

) time.sleep(1)

print

("world"

) endtime = time.time(

) seconds = endtime - starttime

print

("函式執行了{}秒"

.format

(seconds)

)if __name__ ==

'__main__'

: func(

)

如果不想入侵到原來的**,可以將函式作為引數傳入到乙個具有計時功能的函式中
import time

#不改變原函式的**的情況下統計函式執行所需的時間

defmyfunc()

:print

("hello"

) time.sleep(1)

print

("world"

)#不改變原來的函式功能,但是每次都要執行該函式

defdeco

(func)

: starttime = time.time(

)# 執行函式

func(

) endtime = time.time(

) seconds = endtime - starttime

print

("func()執行了{}秒"

.format

(seconds)

)if __name__ ==

'__main__'

: f = myfunc;

#這裡要統計時間的時候,每次都要呼叫deco()函式

deco(f)

這樣有乙個問題就是每次都要呼叫deco()函式來實現計時

使用裝飾器,既不改變原來函式的功能,又不需要重複呼叫deco()函式

import time

defdeco

(func)

:def()

: starttime = time.time(

)# 執行函式

func(

) endtime = time.time(

) seconds = endtime - starttime

print

("func()執行了{}秒"

.format

(seconds)

)@deco

deffunc1()

:print

("hello"

) time.sleep(1)

print

("world"

)if __name__ ==

'__main__'

: func1(

)

目前存在乙個問題,裝飾器可能也會被其他型別得函式呼叫,函式傳參可能會不一樣,如果這個時候我們對其進行了修改,很有可能影響其他已經在使用該函式的使用者。

# 標註固定格式

defdeco

(func)

:def

inner

(*args,

**kwargs)

:'''執行函式之前要做的'''

re = func(

*args,

**kwargs)

'''執行函式之後要做的'''

return re

return inner

import time

deftimer

(func)

:def

inner

(*args,

**kwargs)

: starttime = time.time(

)# 執行函式

re = func(

*args,

**kwargs)

endtime = time.time(

) seconds = endtime - starttime

print

("func()執行了{}秒"

.format

(seconds)

)'''執行函式之後要做的'''

return re

return inner

@timer

deffunc

(a, b, c)

: time.sleep(1)

return a, b, c

@timer

deffunc1

(num)

: res =

for i in

range

(num)

:return res

print

(func(1,

2,3)

)#output:func()執行了1.000331163406372秒(1

,2,3

)print

(func1(3)

)#output:

func(

)執行了0.0秒[0

,1,2

]

python裝飾器和閉包

下面幾個部落格有裝飾器的講解,也包含了裝飾器幾種情況的例子,比如說被裝飾的函式帶引數,裝飾器本身帶引數等。理解python中的裝飾器 python裝飾器學習 例子 其實裝飾器跟設計模式中的裝飾器模式基本一樣,就是在已有的函式上新增新的功能,這也是自己對裝飾器的一點簡陋的理解了。下面是自己寫的簡單例子...

python閉包和裝飾器

要理解裝飾器,就要明白閉包 要明白閉包,首先就要從高階函式和作用域說起 說道作用域,一般會談到legb規則。所謂的legb l locals,當前命名空間 區域性作用域 e enclosing,外部函式的命名空間 外部作用域 g global,全域性的命名空間 b bulit in,內建的命名空間平...

python 閉包和裝飾器

閉包的寫法,兩層函式的巢狀,外部函式返回內部函式的引用,外層函式都帶引數 def 外層函式的名稱 引數 def 內層函式的名稱 pass return 內層函式的引用 def set fun func func 254 defcall fun nonlocal func 修改外層函式的值,並且內部函...