一、 裝飾函數(shù)
(1)概念:
裝飾器本質(zhì)上是一個 Python 函數(shù)或類,它可以讓其他函數(shù)或類在不需要做任何代碼修改的前提下 增加額外功能 ,裝飾器的返回值也是一個 函數(shù)/類對象 。它經(jīng)常用于有切面需求的場景,比如:插入日志、性能測試、事務(wù)處理、緩存、權(quán)限校驗等場景,裝飾器是解決這類問題的絕佳設(shè)計。有了裝飾器,我們就可以抽離出大量與函數(shù)功能本身無關(guān)的雷同代碼到裝飾器中并繼續(xù)重用。概括的講,裝飾器的作用就是 為已經(jīng)存在的對象添加額外的功能 。
使用方法:
- 先定義一個裝飾器(帽子)
- 再定義你的業(yè)務(wù)函數(shù)或者類(人)
- 最后把這裝飾器(帽子)扣在這個函數(shù)(人)頭上
(2)實例:
- 日志打印器
# 這是裝飾器函數(shù),參數(shù) func 是被裝飾的函數(shù)
def
logger
(
func
)
:
def
wrapper
(
*
args
,
**
kw
)
:
print
(
'主人,我準(zhǔn)備開始執(zhí)行:{} 函數(shù)了:'
.
format
(
func
.
__name__
)
)
# 真正執(zhí)行的是這行。
func
(
*
args
,
**
kw
)
print
(
'主人,我執(zhí)行完啦。'
)
return
wrapper
@logger
# =》 add = logger(add)
def
add
(
x
,
y
)
:
print
(
'{} + {} = {}'
.
format
(
x
,
y
,
x
+
y
)
)
# 這是裝飾函數(shù)
def
timer
(
func
)
:
def
wrapper
(
*
args
,
**
kw
)
:
t1
=
time
.
time
(
)
# 這是函數(shù)真正執(zhí)行的地方
func
(
*
args
,
**
kw
)
t2
=
time
.
time
(
)
# 計算下時長
cost_time
=
t2
-
t1
print
(
"花費時間:{}秒"
.
format
(
cost_time
)
)
return
wrapper
import
time
@timer
def
want_sleep
(
sleep_time
)
:
time
.
sleep
(
sleep_time
)
want_sleep
(
10
)
#花費時間:10.000298261642456秒
- 帶參數(shù)的函數(shù)裝飾器
def
say_hello
(
contry
)
:
def
wrapper
(
func
)
:
def
deco
(
*
args
,
**
kwargs
)
:
if
contry
==
"china"
:
print
(
"你好!"
)
elif
contry
==
"america"
:
print
(
'hello.'
)
else
:
return
# 真正執(zhí)行函數(shù)的地方
func
(
*
args
,
**
kwargs
)
return
deco
return
wrapper
# 小明,中國人
@say_hello
(
"china"
)
def
xiaoming
(
)
:
pass
# jack,美國人
@say_hello
(
"america"
)
def
jack
(
)
:
pass
4. 不帶參數(shù)的類裝飾器
基于類裝飾器的實現(xiàn),必須實現(xiàn)
call
和 __init__兩個內(nèi)置函數(shù)。
init
:接收被裝飾函數(shù)
call
:實現(xiàn)裝飾邏輯。
class
logger
(
object
)
:
def
__init__
(
self
,
func
)
:
self
.
func
=
func
def
__call__
(
self
,
*
args
,
**
kwargs
)
:
print
(
"[INFO]: the function {func}() is running..."
\
.
format
(
func
=
self
.
func
.
__name__
)
)
return
self
.
func
(
*
args
,
**
kwargs
)
@logger
def
say
(
something
)
:
print
(
"say {}!"
.
format
(
something
)
)
say
(
"hello"
)
#[INFO]: the function say() is running...
#say hello!
-
帶參數(shù)的類裝飾器
上面不帶參數(shù)的例子,你發(fā)現(xiàn)沒有,只能打印INFO級別的日志,正常情況下,我們還需要打印DEBUG WARNING等級別的日志。這就需要給類裝飾器傳入?yún)?shù),給這個函數(shù)指定級別了。
帶參數(shù)和不帶參數(shù)的類裝飾器有很大的不同。
init
:不再接收被裝飾函數(shù),而是接收傳入?yún)?shù)。
call
:接收被裝飾函數(shù),實現(xiàn)裝飾邏輯。
class
logger
(
object
)
:
def
__init__
(
self
,
level
=
'INFO'
)
:
self
.
level
=
level
def
__call__
(
self
,
func
)
:
# 接受函數(shù)
def
wrapper
(
*
args
,
**
kwargs
)
:
print
(
"[{level}]: the function {func}() is running..."
\
.
format
(
level
=
self
.
level
,
func
=
func
.
__name__
)
)
func
(
*
args
,
**
kwargs
)
return
wrapper
#返回函數(shù)
@logger
(
level
=
'WARNING'
)
def
say
(
something
)
:
print
(
"say {}!"
.
format
(
something
)
)
say
(
"hello"
)
#[WARNING]: the function say() is running...
#say hello!
- 內(nèi)置裝飾器:property
- 如果 decorator本身需要傳入?yún)?shù) ,那就需要編寫一個返回decorator的高階函數(shù)。(即再嵌套一個decorator函數(shù)):
def
log
(
text
)
:
def
decorator
(
func
)
:
@functools
.
wraps
(
func
)
#把原始函數(shù)的__name__等屬性復(fù)制到wrapper()函數(shù)中
def
wrapper
(
*
args
,
**
kw
)
:
print
(
'%s %s():'
%
(
text
,
func
.
__name__
)
)
return
func
(
*
args
,
**
kw
)
return
wrapper
return
decorator
@log
(
'execute'
)
#now = log('execute')(now)
def
now
(
)
:
print
(
'2015-3-25'
)
now
(
)
#execute now():
#2015-3-25
首先執(zhí)行l(wèi)og(‘execute’),返回的是decorator函數(shù),再調(diào)用返回的函數(shù),參數(shù)是now函數(shù),返回值最終是wrapper函數(shù)。
二、閉包
(1)概念:
在一個內(nèi)部函數(shù)中,對外部作用域的變量進行引用,(并且一般外部函數(shù)的返回值為內(nèi)部函數(shù)),那么內(nèi)部函數(shù)就被認為是閉包。
維基百科上的解釋是:
在計算機科學(xué)中,閉包(Closure)是詞法閉包(Lexical Closure)的簡稱, 是引用了自由變量的函數(shù)。這個被引用的自由變量將和這個函數(shù)一同存在, 即使已經(jīng)離開了創(chuàng)造它的環(huán)境也不例外。 所以,有另一種說法認為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實體。
add訪問了外部函數(shù)start的變量,并且函數(shù)返回值為add函數(shù)(python可以
返回函數(shù)
)
閉包,顧名思義,就是一個封閉的包裹,里面包裹著
自由變量
,就像在類里面定義的屬性值一樣,自由變量的可見范圍隨同包裹,哪里可以訪問到這個包裹,哪里就可以訪問到這個自由變量。
再通過Python的語言介紹一下,一個閉包就是你調(diào)用了一個函數(shù)A,這個函數(shù)A返回了一個函數(shù)B給你。這個返回的函數(shù)B就叫做閉包。你在調(diào)用函數(shù)A的時候傳遞的參數(shù)就是自由變量(當(dāng)函數(shù)A的生命周期結(jié)束之后,自由變量依然存在,因為它被閉包引用了,所以不會被回收。)。
(2)常見問題
-
閉包無法修改 外部函數(shù) 的 局部變量 (即add函數(shù)無法修改start函數(shù)定義的變量)
-
閉包使得 局部變量 在 函數(shù)外 被訪問成為可能
-
閉包避免了使用全局變量
-
閉包允許將函數(shù)與其所操作的某些數(shù)據(jù)(環(huán)境)關(guān)連起來。
-
裝飾器就是一種的閉包的應(yīng)用,只不過其傳遞的是 函數(shù) 。
-
閉包的最大特點是可以將 父函數(shù)的變量與內(nèi)部函數(shù)綁定 ,并返回綁定變量后的函數(shù)(也即閉包)。(類似類)
-
python循環(huán)中不包含域的概念。
loop在python中是沒有域的概念的,flist在向列表中添加func的時候,并沒有保存i的值,而是當(dāng)執(zhí)行f(2)的時候才去取,這時候循環(huán)已經(jīng)結(jié)束,i的值是2,所以結(jié)果都是4。
解決辦法:
在func外面再定義一個makefun函數(shù),func形成閉包。
參考:
https://foofish.net/python-closure.html
https://zhuanlan.zhihu.com/p/22229197
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
