python的闭包及装饰器
閉包:
閉包(Closure)是詞法閉包(Lexical Closure)的簡稱,是引用了自由變量的函數。這個被引用的自由變量將和這個函數一同存在,即使已經離開了創造它的環境也不例外。所以,有另一種說法認為閉包是由函數和與其相關的引用環境組合而成的實體
1、函數是一個對象
2、函數執行完成后內部變量回收
3、函數屬性
4、函數的返回值
實例一、
分別檢測分數科目總分為100、150兩種情況的成績
初級代碼如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #?-*-?coding:utf-8?-*- ###檢測分數總數為100分的及格情況 def?ck_100(val): ????passline?=?60 ????if?val?>=?passline: ????????print?"當前科目總分為100,你的成績為:?%d,成績通過?"?%val ????else: ????????print?"當前科目總分為100,你的成績為:?%d,成績不通過?"?%val ###檢測分數總數為150分的及格情況 def?ck_150(val): ????passline?=?90 ????if?val?>=?passline: ????????print?"當前科目總分為150,你的成績為:?%d,成績通過?"?%val ????else: ????????print?"當前科目總分為100,你的成績為:?%d,成績不通過"?%val #100總分科目 ck_100(90) ck_100(55) #150總分科目 ck_150(110) ck_150(88) |
執行結果如下:
| 1 2 3 4 5 6 7 | >>>?================================?RESTART?================================ >>>? 當前科目總分為100,你的成績為:?90,成績通過? 當前科目總分為100,你的成績為:?55,成績不通過? 當前科目總分為150,你的成績為:?110,成績通過? 當前科目總分為100,你的成績為:?88,成績不通過 >>> |
使用閉包優化后的代碼:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #?-*-?coding:utf-8?-*- ###根據不同科目設置及格線 def?set_passline(passline): ###檢測傳入分數是否及格 ????def?cmp(val): ????????if?val?>=?passline: ????????????print?"及格線為:?%d,?您的分數為:?%d?,恭喜及格"?%(passline,val) ????????else: ????????????print?"及格線為:?%d,?您的分數為:?%d?,遺憾不及格"?%(passline,val) ????return?cmp #總分為100的科目 score_100?=?set_passline(60) score_100(89) score_100(55) #總分為150的科目 score_150?=?set_passline(90) score_150(99) score_150(88) |
執行結果:
| 1 2 3 4 5 6 | >>>?================================?RESTART?================================ >>>? 及格線為:?60,?您的分數為:?89?,恭喜及格 及格線為:?60,?您的分數為:?55?,遺憾不及格 及格線為:?90,?您的分數為:?99?,恭喜及格 及格線為:?90,?您的分數為:?88?,遺憾不及格 |
Tips:
可以看代碼量少了一半;
閉包就是我們內置函數對 enclosing 作用域??變量? 的使用
實例二、
| 1 2 3 4 5 6 7 8 9 10 11 | #?-*-?coding:utf-8?-*- ###求成績總分 def?my_sum(*args): ????return?sum(args) ###求成績平均分 def?my_average(*args): ????return?sum(args)/len(args) print?my_sum(1,2,3,4,5) print?my_average(1,2,3,4,5) |
執行結果:
| 1 2 3 4 5 | >>>?================================?RESTART?================================ >>>? 15 3 >>> |
發現上述代碼不夠健壯,如果你傳入字符串或空的列表就會報錯
| 1 | print?my_sum(1,2,3,4,5,'6') |
| 1 2 3 | ????return?sum(args) TypeError:?unsupported?operand?type(s)?for?+:?'int'?and?'str' >>> |
修改代碼,進行檢查傳入數據的類型及長度
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #?-*-?coding:utf-8?-*- ###求成績總分 def?my_sum(*args): ????if?len(args)?==?0: ????????return?0 ????for?val?in?args: ????????if?not?isinstance(val,int): ????????????print?"傳入參數有非整數性..." ????????????return?0 ????return?sum(args) ###求成績平均分 def?my_average(*args): ????if?len(args)?==?0: ????????return?0 ????for?val?in?args: ????????if?not?isinstance(val,int): ????????????print?"傳入參數有非整數性..." ????????????return?0 ????return?sum(args)/len(args) print?my_sum(1,2,3,4,5) print?my_average(1,2,3,4,5) print?my_sum(1,2,3,4,5,'6') |
執行結果:
| 1 2 3 4 5 6 | >>>?================================?RESTART?================================ >>>? 15 3 傳入參數有非整數性... 0 |
健壯性是可以了,不過發現其中有一部門代碼是重復的,但是執行的 函數 又是不同的,分別為 my_sum 、my_average
使用閉包優化后的代碼:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | #?-*-?coding:utf-8?-*- ###求成績總分 def?my_sum(*args): ????return?sum(args) ###求成績平均分 def?my_average(*args): ????return?sum(args)/len(args) def?dec(func): ????def?in_dec(*args): ????????if?len(args)?==?0: ????????????print?"傳入參數列表為0" ????????????return?0 ????????for?val?in?args: ????????????if?not?isinstance(val,int): ????????????????print?"傳入非整數參數" ????????????????return?0 ????????return?func(*args) ????return?in_dec #重新定義my_sum my_sum?=?dec(my_sum) #此處調用dec,作用就是將?參數為函數的?func?變為?in_dec?函數的一個屬性?(?enclosing?屬性?),并且將?in_dec?函數本身返回,返回的函數還具有?in_dec?本身的功能屬性?(檢測參數的作用) #并且?my_sum?進行了重新定義,返回值為?傳入函數處理后的?返回值 print?my_sum(1,2,3,4,5) print?my_sum() print?my_sum(1,2,3,4,5,'6') #重新定義my_average my_average?=?dec(my_average) print?my_average(1,2,3,4,5) print?my_average() print?my_average(1,2,3,4,5,'6') |
執行結果:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | >>>?================================?RESTART?================================ >>>? 15 傳入參數列表為0 0 傳入非整數參數 0 3 傳入參數列表為0 0 傳入非整數參數 0 >>> |
裝飾器:
1、用來裝飾函數
2、返回一個新的函數對象
3、被裝飾函數標識符指向返回的函數對象
4、語法糖: @deco
上述閉包的例子,in_dec 被 dec 所裝飾,那可以說裝飾器其實就是閉包的一個本質使用
為了演示 裝飾器 的功能,以上述代碼為例,進行 裝飾器 寫法
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #?-*-?coding:utf-8?-*- def?dec(func): ????print?"call?dec" ????def?in_dec(*args): ????????print?"call?in_dec" ????????if?len(args)?==?0: ????????????print?"傳入參數列表為0" ????????????return?0 ????????for?val?in?args: ????????????if?not?isinstance(val,int): ????????????????print?"傳入非整數參數" ????????????????return?0 ????????return?func(*args) ????return?in_dec???? @dec ###求成績總分 def?my_sum(*args): ????return?sum(args) ###求成績平均分 def?my_average(*args): ????return?sum(args)/len(args) |
所以沒有顯示調用,但是執行以下:
| 1 2 3 4 | >>>?================================?RESTART?================================ >>>? call?dec >>> |
結果證明 調用 了 dec 函數,那應該是 裝飾器語法糖 @dec 時進行了調用,但是它返回了一個函數,那被誰接受了呢? 是被他裝飾的 my_sum 接受了。
為了證明,來調用下 my_sum
| 1 2 | print?my_sum(1,2,3,4,5) print?my_sum(1,2,3,4,5,'6') |
結果:
| 1 2 3 4 5 6 7 8 9 | >>>?================================?RESTART?================================ >>>? call?dec??????#######?語法糖調用一次裝飾函數?dec call?in_dec 15 call?in_dec 傳入非整數參數 0 >>> |
證明是對的;
@dec 相當于上述的?
| 1 | my_sum?=?dec(my_sum) |
也就可以認為 被重新賦值的 my_sum 相當于裝飾器 dec 的內部函數 in_dec
裝飾器 dec 的參數 func 相當于 被修飾的函數 my_sum
裝飾器 實例二、
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #?-*-?coding:utf-8?-*- def?dec(func): ????print?"call?dec" ????def?in_dec(): ????????print?"call?in_dec" ????????func() @dec def?test_dec(): ????print?"call?test_dec" print?type(test_dec) test_dec() |
執行結果:
| 1 2 3 4 5 6 7 8 9 10 | >>>?================================?RESTART?================================ >>>? call?dec <type?'NoneType'> Traceback?(most?recent?call?last): ??File?"D:\xisuo\xx\q.py",?line?15,?in?<module> ????test_dec() TypeError:?'NoneType'?object?is?not?callable >>> |
可以看到這里的 test_dec 是 NoneType類型,無法調用,那是因為我們在裝飾器函數沒有進行顯示的 return ,python 默認返回 None,
所以裝飾器函數必須顯示的進行 return in_func (這里為 return in_dec)
修改后 執行
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #?-*-?coding:utf-8?-*- def?dec(func): ????print?"call?dec" ????def?in_dec(): ????????print?"call?in_dec" ????????func() ????return?in_dec??????#結論:必須返回 @dec def?test_dec(): ????print?"call?test_dec" print?type(test_dec) test_dec() |
| 1 2 3 4 5 6 7 | >>>?================================?RESTART?================================ >>>? call?dec <type?'function'> call?in_dec call?test_dec >>> |
上面提到了(紅字)函數間的關系(相當于對象引用關系),那賦值的被修飾函數的 參數 就必須三個函數都對應起來
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #?-*-?coding:utf-8?-*- def?dec(func): ????print?"call?dec" ????def?in_dec(x,y):??????##參數要對應起來 ????????print?"call?in_dec" ????????func(x,y)???????##參數要對應起來 ????return?in_dec @dec def?test_dec(x,y):?????##參數要對應起來 ????print?"call?test_dec?",x?+?y print?type(test_dec) test_dec(3,5) |
執行結果:
| 1 2 3 4 5 6 7 | >>>?================================?RESTART?================================ >>>? call?dec <type?'function'> call?in_dec call?test_dec??8 >>> |
本文轉自 西索oO 51CTO博客,原文鏈接:http://blog.51cto.com/lansgg/1884913
總結
以上是生活随笔為你收集整理的python的闭包及装饰器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 类的属性、类的方法、类的内置方法
- 下一篇: Python回顾与整理10:模块