成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

關于Python閉包的一切

開發 后端
任何把函數當做一等對象的語言,它的設計者都要面對一個問題:作為一等對象的函數在某個作用域中定義,但是可能會在其他作用域中調用,如何處理自由變量?

[[402091]]

本文轉載自微信公眾號「dongfanger」,作者dongfanger。轉載本文請聯系dongfanger公眾號。

任何把函數當做一等對象的語言,它的設計者都要面對一個問題:作為一等對象的函數在某個作用域中定義,但是可能會在其他作用域中調用,如何處理自由變量?

自由變量(free variable),未在局部作用域中綁定的變量。

為了解決這個問題,Python之父Guido Van Rossum設計了閉包,有如神來之筆,代碼美學盡顯。在討論閉包之前,有必要先了解Python中的變量作用域。

變量作用域

先看一個全局變量和自由變量的示例:

  1. >>> b = 6 
  2. >>> def f1(a): 
  3. ...     print(a) 
  4. ...     print(b) 
  5. ...      
  6. >>> f1(3) 

函數體外的b為全局變量,函數體內的b為自由變量。因為自由變量b綁定到了全局變量,所以在函數f1()中能正確print。

如果稍微改一下,那么函數體內的b就會從自由變量變成局部變量:

  1. >>> b = 6 
  2. def f1(a): 
  3. ...     print(a) 
  4. ...     print(b) 
  5. ...     b = 9 
  6. ...      
  7. >>> f1(3) 
  8. Traceback (most recent call last): 
  9.   File "<input>", line 1, in <module> 
  10.   File "<input>", line 3, in f1 
  11. UnboundLocalError: local variable 'b' referenced before assignment 

在函數f1()后面加上b = 9報錯:局部變量b在賦值前進行了引用。

這不是缺陷,而是Python設計:Python不要求聲明變量,而是假定在函數定義體中賦值的變量是局部變量。

如果想讓解釋器把b當做全局變量,那么需要使用global聲明:

  1. >>> b = 6 
  2. >>> def f1(a): 
  3. ...     global b 
  4. ...     print(a) 
  5. ...     print(b) 
  6. ...     b = 9 
  7. ...      
  8. >>> f1(3) 

閉包

回到文章開頭的自由變量問題,假如有個叫做avg的函數,它的作用是計算系列值的均值,用類實現:

  1. class Averager(): 
  2.      
  3.     def __init__(self): 
  4.         self.series = [] 
  5.          
  6.     def __call__(self, new_value): 
  7.         self.series.append(new_value) 
  8.         total = sum(self.series) 
  9.         return totle / len(self.series) 
  10.  
  11. avg = Averager() 
  12. avg(10)  # 10.0 
  13. avg(11)  # 10.5 
  14. avg(12)  # 11.0 

類實現不存在自由變量問題,因為self.series是類屬性。但是函數實現,進行函數嵌套時,問題就出現了:

  1. def make_averager(): 
  2.     series = [] 
  3.      
  4.     def averager(new_value): 
  5.         # series是自由變量 
  6.         series.append(new_value) 
  7.         total = sum(series) 
  8.         return totle / len(series) 
  9.      
  10.     return averager 
  11.  
  12. avg = make_averager() 
  13. avg(10)  # 10.0 
  14. avg(11)  # 10.5 
  15. avg(12)  # 11.0 

函數make_averager()在局部作用域中定義了series變量,它的內部函數averager()的自由變量series綁定了這個值。但是在調用avg(10)時,make_averager()函數已經return返回了,它的局部作用域也消失了。沒有閉包的話,自由變量series一定會報錯找不到定義。

那么閉包是怎么做的呢?閉包是一種函數,它會保留定義時存在的自由變量的綁定,這樣調用函數時,雖然定義作用域不可用了,但是仍然能使用那些綁定。

如下圖所示:

閉包會保留自由變量series的綁定,在調用avg(10)時繼續使用這個綁定,即使make_averager()函數的局部作用域已經消失。

nonlocal

把上面示例的需求稍微優化下,只存儲目前的總值和元素個數:

  1. def make_averager(): 
  2.     count = 0 
  3.     total = 0 
  4.      
  5.     def averager(new_value): 
  6.         count += 1 
  7.         total += new_value 
  8.         return total / count 
  9.          
  10.     return averager 

運行后會報錯:局部變量count在賦值前進行了引用。因為count +=1等同于count = count + 1,存在賦值,count就變成局部變量了。total也是如此。

這里如果把count和total通過global關鍵字聲明為全局變量,顯然是不合適的,它們作用域最多只擴展到make_averager()函數內。為了解決這個問題,Python3引入了nonlocal關鍵字聲明:

  1. def make_averager(): 
  2.     count = 0 
  3.     total = 0 
  4.      
  5.     def averager(new_value): 
  6.         nonlocal count, total 
  7.         count += 1 
  8.         total += new_value 
  9.         return total / count 
  10.          
  11.     return averager 

nonlocal的作用是把變量標記為自由變量,即使在函數中為變量賦值了,也仍然是自由變量。

注意,對于列表、字典等可變類型來說,添加元素不是賦值,不會隱式創建局部變量。對于數字、字符串、元組等不可變類型以及None來說,賦值會隱式創建局部變量。示例:

  1. def make_averager(): 
  2.     # 可變類型 
  3.     count = {} 
  4.  
  5.     def averager(new_value): 
  6.         print(count)  # 成功 
  7.         count[new_value] = new_value 
  8.         return count 
  9.  
  10.     return averager 

可變對象添加元素不是賦值,不會隱式創建局部變量。

  1. def make_averager(): 
  2.     # 不可變類型 
  3.     count = 1 
  4.  
  5.     def averager(new_value): 
  6.         print(count)  # 報錯 
  7.         count = new_value 
  8.         return count 
  9.  
  10.     return averager 

count是不可變類型,賦值會隱式創建局部變量,報錯:局部變量count在賦值前進行了引用。

  1. def make_averager(): 
  2.     # None 
  3.     count = None 
  4.  
  5.     def averager(new_value): 
  6.         print(count)  # 報錯 
  7.         count = new_value 
  8.         return count 
  9.  
  10.     return averager 

count是None,賦值會隱式創建局部變量,報錯:局部變量count在賦值前進行了引用。

小結

 

本文先介紹了全局變量、自由變量、局部變量的概念,這是理解閉包的前提。閉包就是用來解決函數嵌套時,自由變量如何處理的問題,它會保留自由變量的綁定,即使局部作用域已經消失。對于不可變類型和None來說,賦值會隱式創建局部變量,把自由變量轉換為局部變量,這可能會導致程序報錯:局部變量在賦值前進行了引用。除了使用global聲明為全局變量外,還可以使用nonlocal聲明把局部變量強制變為自由變量,實現閉包。

 

責任編輯:武曉燕 來源: dongfanger
相關推薦

2020-09-11 10:55:10

useState組件前端

2021-02-28 09:47:54

軟件架構軟件開發軟件設計

2018-11-23 11:17:24

負載均衡分布式系統架構

2021-02-19 23:08:27

軟件測試軟件開發

2020-10-14 08:04:28

JavaScrip

2022-08-21 17:35:31

原子多線程

2023-04-20 10:15:57

React組件Render

2022-04-02 09:38:00

CSS3flex布局方式

2022-08-17 06:25:19

偽共享多線程

2018-01-17 09:15:52

負載均衡算法

2023-04-12 14:04:48

光纖網絡

2023-02-10 08:44:05

KafkaLinkedIn模式

2018-01-05 14:23:36

計算機負載均衡存儲

2023-07-10 10:36:17

人工智能AI

2021-08-09 14:40:02

物聯網IOT智能家居

2012-12-31 11:22:58

開源開放

2021-10-05 21:03:54

BeautifulSo 爬蟲

2022-04-24 09:00:00

滲透測試安全數字時代

2022-12-30 11:24:21

2023-12-11 16:36:09

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久久精品免费观看 | 欧美日本韩国一区二区 | 韩国av网站在线观看 | av在线免费观看网站 | 少妇精品久久久久久久久久 | 久久一区二区三区电影 | 福利网址| 逼逼网| 色吧色综合 | 蜜桃视频一区二区三区 | 国产成人综合在线 | 一级做a爰片久久毛片免费看 | av大片| 免费视频一区 | 日本一区二区电影 | 欧美a级成人淫片免费看 | 国产精品精品久久久久久 | 亚洲成年人免费网站 | 亚洲欧美国产精品久久 | 国产欧美在线一区二区 | 欧美一区二区视频 | 超碰男人天堂 | 一级在线免费观看 | 99久久国产综合精品麻豆 | 久久久国产一区二区三区 | 成人深夜福利在线观看 | 国产一区二区三区免费观看在线 | 国产成人精品一区二区三区网站观看 | 国产成人a亚洲精品 | 亚洲精品性视频 | 自拍第1页 | 日韩精品一区二区三区在线观看 | 视频一区二区在线观看 | 日韩免费一区 | 久久精品—区二区三区 | 青青草视频网 | 天天搞天天操 | 亚洲午夜视频 | 国产 欧美 日韩 一区 | 国产精品亚洲成在人线 | 天堂一区二区三区 |