日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

Python之闭包与装饰器

發布時間:2024/3/7 python 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python之闭包与装饰器 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

閉包

由于閉包這個概念比較難以理解,尤其是初學者來說,相對難以掌握,所以我們通過示例去理解學習閉包。

給大家提個需求,然后用函數去實現:完成一個計算不斷增加的系列值的平均值的需求。

例如:整個歷史中的某個商品的平均收盤價。什么叫平局收盤價呢?就是從這個商品一出現開始,每天記錄當天價格,然后計算他的平均值:平均值要考慮直至目前為止所有的價格。

比如大眾推出了一款新車:小白轎車。

第一天價格為:100000元,平均收盤價:100000元

第二天價格為:110000元,平均收盤價:(100000 + 110000)/2 元

第三天價格為:120000元,平均收盤價:(100000 + 110000 + 120000)/3 元

series = [] def make_averager(new_value):series.append(new_value)total = sum(series)return total / len(series)print(make_averager(100000)) print(make_averager(110000)) print(make_averager(120000))

從上面的例子可以看出,基本上完成了我們的要求,但是這個代碼相對來說是不安全的,因為你的這個series列表是一個全局變量,只要是全局作用域的任何地方,都可能對這個列表進行改變。

series = [] def make_averager(new_value):series.append(new_value)total = sum(series)return total / len(series)print(make_averager(100000)) print(make_averager(110000)) series.append(666) # 如果對數據進行相應改變,那么你的平均收盤價就會出現很大的問題。 print(make_averager(120000))

那么怎么辦呢?有人說,你把他放在函數中不就行了,這樣不就是局部變量了么?數據不就相對安全了么?

def make_averager(new_value):series = []series.append(new_value)total = sum(series)return total / len(series)print(make_averager(100000)) # 100000.0 print(make_averager(110000)) # 110000.0 print(make_averager(120000)) # 120000.0

這樣計算的結果是不正確的,那是因為執行函數,會開啟一個臨時的名稱空間,隨著函數的結束而消失,所以你每次執行函數的時候,都是重新創建這個列表,那么這怎么做呢?這種情況下,就需要用到我們講的閉包了,我們用閉包的思想改一下這個代碼。

def make_averager():series = []def averager(new_value):series.append(new_value)total = sum(series)return total/len(series)return averageravg = make_averager() print(avg(100000)) print(avg(110000)) print(avg(120000))

大家仔細看一下這個代碼,我是在函數中嵌套了一個函數。那么avg 這個變量接收的實際是averager函數名,也就是其對應的內存地址,我執行了三次avg 也就是執行了三次averager這個函數。那么此時你們有什么問題?

肯定有學生就會問,那么我的make_averager這個函數只是執行了一次,為什么series這個列表沒有消失?反而還可以被調用三次呢?這個就是最關鍵的地方,也是閉包的精華所在。我給大家說一下這個原理,以圖為證:

? 上面被紅色方框框起來的區域就是閉包,被藍色圈起來的那個變量應該是make_averager()函數的局部變量,它應該是隨著make_averager()函數的執行結束之后而消失。但是他沒有,是因為此區域形成了閉包,series變量就變成了一個叫自由變量的東西,averager函數的作用域會延伸到包含自由變量series的綁定。也就是說,每次我調用avg對應的averager函數 時,都可以引用到這個自用變量series,這個就是閉包。

閉包的定義:

? 1. 閉包是嵌套在函數中的函數。

? 2. 閉包必須是內層函數對外層函數的變量(非全局變量)的引用。

如何判斷判斷閉包?舉例讓同學回答:

# 例一: def wrapper():a = 1def inner():print(a)return inner ret = wrapper()# 例二: a = 2 def wrapper():def inner():print(a)return inner ret = wrapper()# 例三:def wrapper(a,b):def inner():print(a)print(b)return inner a = 2 b = 3 ret = wrapper(a,b)

以上三個例子,最難判斷的是第三個,其實第三個也是閉包,如果我們每次去研究代碼判斷其是不是閉包,有一些不科學,或者過于麻煩了,那么有一些函數的屬性是可以獲取到此函數是否擁有自由變量的,如果此函數擁有自由變量,那么就可以側面證明其是否是閉包函數了(了解):

def make_averager():series = []def averager(new_value):series.append(new_value)total = sum(series)return total/len(series)return averager avg = make_averager() # 函數名.__code__.co_freevars 查看函數的自由變量 print(avg.__code__.co_freevars) # ('series',) 當然還有一些參數,僅供了解:# 函數名.__code__.co_varnames 查看函數的局部變量 print(avg.__code__.co_varnames) # ('new_value', 'total') # 函數名.__closure__ 獲取具體的自由變量對象,也就是cell對象。 # (<cell at 0x0000020070CB7618: int object at 0x000000005CA08090>,) # cell_contents 自由變量具體的值 print(avg.__closure__[0].cell_contents) # []

閉包的作用:保存局部信息不被銷毀,保證數據的安全性。

閉包的應用

  • 可以保存一些非全局變量但是不易被銷毀、改變的數據。
  • 裝飾器。
  • 閉包的解釋:

    閉包是存在嵌套函數當中的 內層對外層非全局變量的的引用 ,稱之為自由變量 它不會隨著函數的結束而消失,一直保存在內存,最終的目的是保證數據的安全

    裝飾器

    開放封閉原則

    軟件面世時,不可能吧所有的功能都設計好,當前的未來一兩年功能給你上線,定期更新迭代.對于軟件之前寫的源代碼一般都不會修改,對函數里面的代碼以及函數的調用方式.

    開放原則: 在源碼不改變的情況下,增加一些額外的功能

    封閉原則: 不要改變源代碼

    python中裝飾器: 完美詮釋的開放封閉原則

    裝飾器 就是個函數 : 他要裝飾一個函數,在不改變原函數以及調用方式的前提下,給其增加一個額外的功能

    裝飾器初識

    # 1 李業,在一家xx科技有限公司工作,主管安排了一個任務, # 寫一個代碼測試懟懟哥寫的函數的執行效率。 # import time # def index(): # time.sleep(2) # print('歡迎訪問博客園首頁')# print(time.time()) # start_time = time.time() # index() # end_time = time.time() # print(f'此函數的執行效率{end_time-start_time}')# 2. 主管讓你測試小鄧,李大象,重復代碼太多。 # # def func1(): # time.sleep(2) # print('歡迎訪問日記首頁') # # # def func2(): # time.sleep(1) # print('歡迎訪問評論首頁')# start_time = time.time() # func1() # end_time = time.time() # print(f'此函數的執行效率{end_time-start_time}') # # start_time = time.time() # func2() # end_time = time.time() # print(f'此函數的執行效率{end_time-start_time}')# 3. 整合到函數中# def func1(): # time.sleep(2) # print('歡迎訪問日記首頁') # # # def func2(): # time.sleep(1) # print('歡迎訪問評論首頁')# def test_time(x): # start_time = time.time() # x() # end_time = time.time() # print(f'此函數的執行效率{end_time-start_time}')# test_time(func1) # test_time(func2)# 4. 懟懟哥這個函數在實際項目中被500執行,主管要求:在被執行此函數時, # 同時要測試一下被執行函數的效率。# def index(): # time.sleep(2) # print('歡迎訪問博客園首頁') # # # index() # def test_time(x): # start_time = time.time() # x() # end_time = time.time() # print(f'此函數的執行效率{end_time-start_time}') # # test_time(index)# 版本4的問題: 開放原則滿足了,封閉原則:不改變原函數的源碼,以及調用方式。 # 違反了封閉原則:改變了函數的調用方式。# 版本5: 不能改變原函數的調用方式(閉包):# def index(): # time.sleep(2) # print('歡迎訪問博客園首頁') # # # index() # # # def func1(): # # time.sleep(2) # # print('歡迎訪問日記首頁') # # def test_time(x): # x = index # def inner(): # start_time = time.time() # x() # end_time = time.time() # print(f'此函數的執行效率{end_time-start_time}') # return inner # # index = test_time(index) # index()# 語法糖 @加上裝飾器函數的名# def f(): # print(666) # # # f = '太白' # print(f)# def test_time(x): # x = index # def inner(): # start_time = time.time() # x() # end_time = time.time() # print(f'此函數的執行效率{end_time-start_time}') # return inner # # # # @test_time # index = test_time(index) # def index(): # time.sleep(2) # print('歡迎訪問博客園首頁')# index = test_time(index) # index()# def func1(): # time.sleep(2) # print('歡迎訪問日記首頁')# @test_time # def func2(): # time.sleep(1) # print('歡迎訪問評論首頁')# func2 = test_time(func2) # func3 = test_time(func3) # func2() '''100行代碼''' # index()'''10行代碼''' # index()'''50行代碼''' # index()# 版本6:被裝飾函數有返回值# def test_time(x): # x = index # def inner(): # start_time = time.time() # ret = x() # # print(F'ret: {ret}') # end_time = time.time() # print(f'此函數的執行效率{end_time-start_time}') # return ret # return inner # # # @test_time # index = test_time(index) # def index(): # time.sleep(0.5) # print('歡迎訪問博客園首頁') # return True # # print(index()) # inner() # 你應該是讓True返回給index()這樣才完美了,但是現在index是inner,所以你要是完全不改變原函數的使用, # 你print(index()) ---> True# 版本7: 被裝飾函數帶參數,無論加不加裝飾器,你的實參'太白金星'應該傳給形參n,。 # 但版本6不能實現傳參,index('太白金星') == inner('太白金星') # # def test_time(x): # x = index # def inner(*args,**kwargs): # # 函數的定義:* ** 聚合。 # # args = ('蘋果') # #args = (1, 3) # start_time = time.time() # ret = x(*args,**kwargs) # # 函數的執行:* ** 打散。 # # ret = x(*('蘋果')) ==x('蘋果',) # # ret = x(*(1, 3)) ==x(1,3) # # print(F'ret: {ret}') # end_time = time.time() # print(f'此函數的執行效率{end_time-start_time}') # return ret # return inner # # # # @test_time # index = test_time(index) # def index(n): # time.sleep(0.5) # print(f'歡迎{n}訪問博客園首頁') # return True # # # @test_time # index = test_time(index) # def func2(a,b): # time.sleep(0.5) # print(f'最終結果:{a+b}') # return a + b # # # print(index('蘋果')) # inner('蘋果') # print(func2(1,3)) # == inner(1,3)# def warpper(f): # def inner(*args,**kwargs): # '''被裝飾函數之前的操作''' # # print(666) # ret = f(*args,**kwargs) # '''被裝飾函數之后的操作''' # # print('執行完畢了') # return ret # return inner # # @warpper # def func(): # print(111) # # # func() # func() # func() # func() # func()# 裝飾器的應用:在不改變原函數的源碼以及調用方式前提下,為其增加額外的功能。 # 登陸認證,打印日志等。

    帶參數裝飾器

    def wrapper(f):def inner(*args,**kwargs):if f.__name__ == 'qq':ret = f(*args,**kwargs)return retelse:return innerdef wrapper(f):def inner(*args, **kwargs):ret = f(*args, **kwargs)return retreturn innerdef wrapper_out(n,*args,sex='男',):def wrapper(f): # fdef inner(*args,**kwargs):ret = f(*args,**kwargs) # func1()return retreturn innerreturn wrapperdef func1():print('in func1') func = wrapper_out(1) # wrapper函數名 ly = func(func1) # wrapper(func1) = inner ly() # inner()def wrapper_out(n):def wrapper(f):def inner(*args,**kwargs):# if n == 'qq':# username = input('請輸入用戶名:').strip()# password = input('請輸入密碼:').strip()# with open('qq',encoding='utf-8') as f1:# for line in f1:# user,pwd = line.strip().split('|')# if username == user and password == pwd:# print('登陸成功')# ret = f(*args,**kwargs)# return ret# return False# elif n == 'tiktok':# username = input('請輸入用戶名:').strip()# password = input('請輸入密碼:').strip()# with open('tiktok', encoding='utf-8') as f1:# for line in f1:# user, pwd = line.strip().split('|')# if username == user and password == pwd:# print('登陸成功')# ret = f(*args, **kwargs)# return ret# return Falseusername = input('請輸入用戶名:').strip()password = input('請輸入密碼:').strip()with open(n,encoding='utf-8') as f1:for line in f1:user,pwd = line.strip().split('|')if username == user and password == pwd:print('登陸成功')ret = f(*args,**kwargs)return retreturn Falsereturn innerreturn wrapper """ # @wrapper_out('qq') # def qq(): # print('成功訪問qq') # qq() # 看到帶參數的裝飾器分兩步執行: ''' @wrapper_out('騰訊')1. 執行wrapper_out('騰訊') 這個函數,把相應的參數'騰訊' 傳給 n,并且得到返回值 wrapper函數名。2. 將@與wrapper結合,得到我們之前熟悉的標準版的裝飾器按照裝飾器的執行流程執行。 ''' """@wrapper_out('qq') def qq():print('成功訪問qq')@wrapper_out('tiktok') def tiktok():print('成功訪問抖音')qq() tiktok() 開發思路:增強耦合性

    多個裝飾器裝飾一個函數

    def wrapper1(func1): # func1 = f原函數def inner1():print('wrapper1 ,before func') # 2func1()print('wrapper1 ,after func') # 4return inner1def wrapper2(func2): # func2 == inner1def inner2():print('wrapper2 ,before func') # 1func2() # inner1print('wrapper2 ,after func') # 5return inner2@wrapper2 # f = wrapper2(f) 里面的f == inner1 外面的f == inner2 @wrapper1 # f = wrapper1(f) 里面的f == func1 外面的 f == inner1 def f():print('in f') # 3f() # inner2()

    轉載于:https://www.cnblogs.com/Jacob-yang/p/11107496.html

    總結

    以上是生活随笔為你收集整理的Python之闭包与装饰器的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。