閉包和裝飾器
閉包
定義:在函數(shù)嵌套的前提下斩例,內(nèi)部函數(shù)使用了外部函數(shù)的變量娃胆,并且外部函數(shù)返回了內(nèi)部函數(shù)接箫,我們把這個(gè)使用外部函數(shù)變量的內(nèi)部函數(shù)稱為閉包博投。
-
閉包的條件:
- 函數(shù)嵌套
- 內(nèi)部函數(shù)使用外部函數(shù)的變量或者參數(shù)
- 外部函數(shù)返回內(nèi)部函數(shù)的引用(即內(nèi)部函數(shù)的內(nèi)存地址)
-
簡(jiǎn)單的閉包示例代碼:
def func_out(): num1 = 10 def func_inner(num2): result = num1 + num2 print(result) return func_inner inner = func_out() inner(50)
-
閉包的作用:
- 閉包可以保存外部函數(shù)內(nèi)的變量,不會(huì)隨著外部函數(shù)調(diào)用完而銷毀
閉包的缺點(diǎn):由于閉包引用了外部函數(shù)的變量加袋,則外部函數(shù)的變量沒有及時(shí)釋放凛辣,消耗內(nèi)存。
-
閉包的應(yīng)用場(chǎng)景:
- 閉包是在不改變?cè)泻瘮?shù)的調(diào)用方法和代碼的基礎(chǔ)上职烧,對(duì)原有的功能進(jìn)行增加
- 閉包大大的增加了代碼的重用性蟀给,一個(gè)寫好的函數(shù)可以供多人調(diào)用,而且還不會(huì)更改代碼的結(jié)構(gòu)
-
內(nèi)部函數(shù)如何修改外部函數(shù)的變量
方法:使用nonlocal
示例代碼:
def fun_out(): num = 10 def funn_inner(): nonlocal num num = 100 print(num) print(num) funn_inner() print(num) return funn_inner inner = fun_out() inner()
裝飾器
定義:就是給已有函數(shù)增加額外功能的函數(shù)阳堕,它本質(zhì)上就是一個(gè)閉包函數(shù)跋理。
-
裝飾器的特點(diǎn):
- 不修改已有函數(shù)的的源代碼
- 不修改已有函數(shù)的調(diào)用方式
- 給已有的函數(shù)增加額外的功能
-
裝飾器的示例代碼:
# 寫個(gè)閉包 def fun(func): def inner(): print("登錄") func() print("評(píng)論成功") return inner # 被裝飾的函數(shù) @fun # comment = fun(comment) 就相當(dāng)于@fun def comment(): print("發(fā)表評(píng)論") # comment = fun(comment) comment() # 裝飾器和閉包的區(qū)別: 1.裝飾器是一種閉包 # 2.裝飾器的外部函數(shù)的參數(shù)是被裝飾的函數(shù) # 3.其實(shí)就是把內(nèi)部函數(shù)賦值給與被裝飾函數(shù)同名的函數(shù)名,然后正常調(diào)用被裝飾的函數(shù)
示例說明:
- 閉包函數(shù)有且只有一個(gè)參數(shù)恬总,必須是函數(shù)類型前普,這樣定義的函數(shù)才是裝飾器。
- 寫代碼要遵循開放封閉原則壹堰,它規(guī)定已經(jīng)實(shí)現(xiàn)的功能代碼不允許被修改拭卿,但可以被擴(kuò)展。
- @fun語(yǔ)法糖是另一種裝飾器的寫法贱纠,和傳統(tǒng)的寫法是一樣的峻厚。以后我們經(jīng)常用到的也會(huì)是語(yǔ)法糖的格式
-
裝飾器的使用場(chǎng)景:
-
函數(shù)執(zhí)行時(shí)間的統(tǒng)計(jì)
import time def make_time(fun): def inner(): start = time.time() fun() end = time.time() print(end - start) return inner @make_time def test(): for i in range(10000000): print(i) test()
輸出日志文件
-
-
裝飾帶有參數(shù)的函數(shù)
-
示例代碼:
def log(fun): def add_lig(num1, num2): # 因?yàn)檫@里相當(dāng)于用inner來替代test,所以test有幾個(gè)參數(shù)谆焊,inner就應(yīng)該有幾個(gè)參數(shù) print("這是一個(gè)加法運(yùn)算") fun(num1, num2) return add_lig @log def test(num1, num2): result = num1 + num2 print(result) test(10, 20) # 結(jié)論:內(nèi)部函數(shù)的參數(shù)必須與被裝飾的函數(shù)的參數(shù)一致,也要與內(nèi)部函數(shù)調(diào)用的被裝飾的函數(shù)的參數(shù)一致
-
-
裝飾帶有返回值的函數(shù)
-
示例代碼:
def log(fun): def add_lig(num1, num2): # 因?yàn)檫@里相當(dāng)于用inner來替代test惠桃,所以test有幾個(gè)參數(shù),inner就應(yīng)該有幾個(gè)參數(shù) print("這是一個(gè)加法運(yùn)算") return fun(num1, num2) return add_lig @log def test(num1, num2): result = num1 + num2 return result rs = test(10, 20) print(rs) # 結(jié)論:如果被裝飾的函數(shù)有返回值辖试,那么內(nèi)部函數(shù)中就必須有返回值.
-
-
通用裝飾器
通用裝飾器就是被裝飾函數(shù)是可以傳遞任意參數(shù)的函數(shù)辜王,
-
示例代碼:
def log(fun): def inner(num, *args, **kwargs): print("正在計(jì)算中") return fun(num,*args, **kwargs) return inner def test(num, *args, **kwargs): result = num for num in args: result += num for num in kwargs.values(): result += num return result result = test(10, 20, 30, a=20, b=30) print(result) # 總結(jié):內(nèi)部函數(shù),被裝飾函數(shù),內(nèi)部函數(shù)調(diào)用的函數(shù) 他們的參數(shù)必須一模一樣
-
多裝飾器的使用:
多裝飾器就是多個(gè)裝飾器同時(shí)裝飾一個(gè)函數(shù)
-
代碼示例:
# 使用div標(biāo)簽把內(nèi)容包裹起來 def make_div(fun): print("--------") def inner(): result = "<div>" + fun() + "</div>" return result return inner # 使用p標(biāo)簽把內(nèi)容包裹起來 def make_p(fun): print("+++++") def inner(): result = "<p>" + fun() + "</p>" return result return inner @make_div @make_p def content(): return "這是多裝飾器的使用代碼" rs = content() print(rs) # 總結(jié): 多重裝飾器先使用靠近被裝飾函數(shù)的裝飾器,類似于穿衣服罐孝,從下到上呐馆,從里到外
代碼說明:當(dāng)你調(diào)用content函數(shù)的時(shí)候,函數(shù)會(huì)先執(zhí)行離它最近的那個(gè)裝飾器make_P里面的代碼莲兢,也就是U先打印"--------"汹来,然后內(nèi)容變成了<p>這是多裝飾器的使用代碼</p>,然后會(huì)執(zhí)行make_div里面的代碼,打印出+++++改艇,最后打印<div><p>這是多裝飾器的使用代碼</p></div>收班。
-
由前面的學(xué)習(xí)內(nèi)容可以知道裝飾器的外部函數(shù)能接受一個(gè)類型為函數(shù)的參數(shù)。即引用被裝飾的函數(shù)遣耍。
那么裝飾器的外部函數(shù)能不能直接傳遞其他的參數(shù)呢闺阱?
答案是:不能。
代碼如下:
def decorator(fn, flag): def inner(num1, num2): if flag == "+": print("--正在努力加法計(jì)算--") elif flag == "-": print("--正在努力減法計(jì)算--") result = fn(num1, num2) return result return inner @decorator('+') def add(a, b): result = a + b return result result = add(1, 3) print(result)
這里運(yùn)行之后舵变,會(huì)報(bào)錯(cuò)酣溃。那要怎樣才能實(shí)現(xiàn)裝飾器里面?zhèn)鬟f參數(shù)呢瘦穆?
在裝飾器的最前面再加上一個(gè)函數(shù),這個(gè)函數(shù)里面可以傳參赊豌。代碼如下:
# 帶有參數(shù)的裝飾器扛或,要在裝飾器前面加上一個(gè)函數(shù),結(jié)束的時(shí)候返回一個(gè)函數(shù) def log(n): def test(fun): def inner(num1, num2): if n == "+": print("正在計(jì)算加法碘饼。") return fun(num1, num2) if n == "-": print("正在計(jì)算減法") return fun(num1, num2) return inner return test def sum_num(num1,num2): result = num1 + num2 return result test = log("+") sum_num = test(sum_num) @log("-") def sub_num(num1,num2): result = num1 - num2 return result rs = sum_num(20, 10) print(rs) rs1 = sub_num(20, 10) print(rs1)
這樣就能在裝飾器里面?zhèn)鬟f其他的參數(shù)了熙兔。
注意點(diǎn):
- 調(diào)用裝飾器后@后要調(diào)用最外部的那個(gè)函數(shù),不再是之前的那個(gè)函數(shù)了艾恼。
- 一定要返回之前的那個(gè)裝飾器函數(shù)的引用住涉。
今天的分享就到這里了,如果哪里出錯(cuò)了钠绍,還請(qǐng)?jiān)谠u(píng)論區(qū)指正出來舆声,我會(huì)積極改正。并且歡迎探討python有關(guān)的知識(shí)柳爽。