Python/函數的嵌套和閉包


Python基礎--函數的嵌套和閉包

1、名稱空間與作用域

 1 名稱空間分為:

 

   1 內置名稱空間   內置在解釋器中的名稱

 

   2 全局名稱空間   頂頭寫的名稱

 

   3 局部名稱空間

 

2 找一個名稱的查找順序:先在局部名稱空間找,再到全局名稱空間找,再到內置名稱空間

 

3 Globals()  查看全局名稱空間的內容

 

  Locals()   查看局部名稱空間的內容

 

4 全局作用域包含內置名稱空間和全局名稱空間

 

   局部作用域包含局部名稱空間

 

 

1 查看內建名稱空間的內容:

 

 

2 作用域

1 x=1
2 def foo():
3     x=100
4     print(x)
5 foo()
6 print(x)

 

輸出結果

1 100
2 1

 

 

3 一定要注意函數要先定義,后使用

1 def foo():
2     print("from foo")
3     bar()
4 def bar():
5     print("from bar")
6 foo()

 

輸出結果:

1 from foo
2 from ba

 

 

1 def foo():
2     print("from foo")
3     bar()
4 foo()
5 def bar():
6 print("from bar")

 

輸出結果:

1 NameError: name 'bar' is not defined  #報錯

 

 

4 Globals()  查看全局名稱空間的內容

  Locals()   查看局部名稱空間的內容

1 x=1
2 def func():
3     print("from func")
4     x=2
5     print(globals())
6     print(locals())
7 func()
8 print(globals())
9 print(locals())

 

輸出結果:

1 from func
2 {'x': 1, '__cached__': None...
3 {'x': 2}
4 {'x': 1, '__cached__': None...
5 {'x': 1, '__cached__': None...

 

 

2、函數嵌套與靜態嵌套域

嵌套調用

嵌套調用作用:將一個大的功能細化成各種小的函數功能並調用

 1 def my_max(x,y):
 2     res=x if x >y else y
 3     return res
 4 print(my_max(10,100))
 5 def my_max4(a,b,c,d):
 6     res1=my_max(a,b)
 7     res2=my_max(res1,c)
 8     res3=my_max(res2,d)
 9     return res3
10 print(my_max4(1,20,3,4))

 

輸出結果:

1 100
2 20

 

在函數內定義的函數 在外面不能用到

1 def f1():
2     def f2():
3         def f3():
4             pass
5     print("---->f1")
6     f2()
7 f2()

 

輸出結果:

1 NameError: name 'f2' is not defined  #報錯

 

#嵌套定義  

x找值的過程:先在局部名稱空間找,再到上一級的局部名稱空間找,再到全局名稱空間找,再到內置名稱空間

 1 x=0
 2 def f1():
 3     #x=1
 4     print("---f1---",x)
 5     def f2():
 6         #x=2
 7         print("---f2---",x)
 8         def f3():
 9             #x=3
10             print("---f3---",x)
11         f3()
12     f2()
13 f1()

 

輸出結果:

1 ---f1--- 0
2 ---f2--- 0
3 ---f3--- 0

 

 

3、函數對象

函數被稱為第一類對象,函數可以被當做數據傳遞

(1)函數可以被賦值

直接輸出函數名的值: 就是函數在內存中的地址

1 def foo():
2     print("foo")
3 print(foo)

 

輸出結果: 

1 <function foo at 0x000000DD32CAAD90>

 

 

函數可以被賦值:將函數名代碼的值賦給變量 

1 def foo():
2     print("foo")
3 f=foo
4 print(f)
5 f()

 

輸出結果:

1 <function foo at 0x0000003AEBEBAD90>
2 foo

 

(2)函數可以作為參數傳遞

函數可以作為參數傳遞

1 def foo():
2     print("foo")
3 def bar(func):
4     print(func)
5     func()
6 print(bar(foo))

 

輸出結果:

1 <function foo at 0x000000A351B4AD90>
2 foo
3 None

 

(3)函數可以作為返回值

函數可以作為函數的返回值

1 def foo():
2     print("foo")
3 def bar(func):
4     print(func)
5     func()
6     return func
7 f=bar(foo)
8 print(f)
9 f()

 

輸出結果:

1 <function foo at 0x000000BFD82CAD90>
2 foo
3 <function foo at 0x000000BFD82CAD90>
4 foo

 

(4)函數可以作為容器類型的元素

函數作為字典的鍵的值:

 1 def add():
 2     print("=======>function add")
 3 def search():
 4     print("=======>function search")
 5 def delete():
 6     print("=======>function delete")
 7 def change():
 8     print("=======>function change")
 9 def tell_msg():
10     msg='''
11     search:查詢
12     add:添加
13     delete:刪除
14     change:修改
15     create:新建
16     '''
17     print(msg)
18 def create():
19     print('=======>function create')
20  
21 cmd_dic={
22     'search':search,
23     'add':add,
24     'delete':delete,
25     'change':change,
26     'create':create
27 }
28 while True:
29     tell_msg()
30     choice=input("please input your choice:")
31     cmd_dic[choice]()

 

 

4、函數閉包

 

(1)函數閉定義

閉包:首先必須是內部定義的函數,該函數包含對外部作用域而不是全局作用域名字的引用

定義:內部函數的代碼包含對外部函數的代碼的引用,但一定不是對全局作用域的引用

閉包的基本形式是:

  在函數F1中,定義F2,F2只能引用F1定義的變量,之后F1函數返回F2的函數名字

  這樣就保證了可以將F1的執行結果賦予給一個變量,該變量可以在之后的任何時刻隨時可以運行

使用閉包的好處:自帶狀態即變量,可以不用傳參就用,方便。

閉包(closure)是函數式編程的重要的語法結構。不同的語言實現閉包的方式不同。Python以函數對象為基礎,為閉包這一語法結構提供支持的 (我們在特殊方法與多范式中,已經多次看到Python使用對象來實現一些特殊的語法)。Python一切皆對象,函數這一語法結構也是一個對象。在函數對象中,我們像使用一個普通對象一樣使用函數對象,比如更改函數對象的名字,或者將函數對象作為參數進行傳遞。

 (2)簡單閉包舉例

1 x=1000
2 def f1():
3     x=1
4     def f2():
5         print(x)
6     return f2
7 f=f1()
8 print(f)
9 x=123

 

輸出結果:

1 <function f1.<locals>.f2 at 0x000000C02BD1AF28>
2 1

 

(3)閉包的__closure__變量

閉包都有__closure__屬性

__closure__對象會返回閉包應用外圍作用域的變量信息。f.__closure__保存外圍作用域的變量內存地址,f.__closure__[0].cell_contents存放的是外圍作用域的變量的值。

對於那些不是閉包的函數對象來說,__closure__ 屬性值為 None。

 1 x=1
 2 def f1():
 3     x=1000
 4     y=2
 5     def f2():
 6         y
 7         print(x)
 8     return f2
 9 f=f1()  #f ---> 內部的函數f2
10 f()
11 print(f.__closure__)
12 print(f.__closure__[0])
13 print(f.__closure__[0].cell_contents)
14 print(f.__closure__[1])
15 print(f.__closure__[1].cell_contents)

 

輸出結果:

1 1000
2 (<cell at 0x000000429E165D68: int object at 0x000000429E0A9EB0>, <cell at 0x000000429E165D98: int object at 0x0000000059C0EF50>)
3 <cell at 0x000000429E165D68: int object at 0x000000429E0A9EB0>
4 1000
5 <cell at 0x000000429E165D98: int object at 0x0000000059C0EF50>
6 2

 

舉例:__closure__ 屬性值為 None

1 x=1
2 def f1():
3     def f2():
4         print(x)
5     return f2
6 f=f1()  #f ---> 內部的函數f2
7 f()
8 print(f.__closure__)

 

 輸出結果為:

1 1
2 None

 

(4)閉包應用

Windows中cmd中執行pip install requests 安裝requests庫件

爬baidu網站的程序

1 from  urllib.request import urlopen
2 def get(url):
3     return urlopen(url).read()
4 print(get('http://www.baidu.com'))
5 print(get('http://www/python.org'))

 

將上面”爬百度”的程序修改成閉包模式:

1 from  urllib.request import urlopen
2 def f1(url):
3     def f2():
4         print(urlopen(url).read())
5     return f2
6 baidu=f1('http://www.baidu.com')
7 python=f1('http://www.python.org')
8 baidu()
9 python()

 

一、名稱空間和作用域

 名稱空間:Python所有有關命名的操作都是在操作名稱空間,例如變量名,函數名 1、內置名稱空間:Python解釋器提供好的功能,解釋器啟動跟着一起啟動,是全局作用域 2、全局名稱空間:Python中頂行寫的,不在函數內部定義的,都是全局名稱空間,在運行的時候會產生名稱空間,是全局作用域 3、局部名稱空間:在一個小范圍定義,只能當前范圍及其子空間內運行,例如在函數內部定義的,是局部作用域

二、函數的嵌套

1、函數的嵌套調用 2、函數的嵌套定義
 1 x = 1111111111111111
 2 def f1():
 3     #x=1
 4     print('------>f1 ',x)
 5     def f2():
 6         #x = 2
 7         print('---->f2 ',x)
 8         def f3():
 9             x=3
10             print('-->f3 ',x)
11         f3()     
12     f2()
13 f1()

三、函數的使用

1、函數可以被當做變量賦值
 1 def foo():
 2     print("foo")
 3 print(foo)          #打印foo的內存地址空間,函數的名字在不帶小括號時表示的是函數的內存地址
 4 f=foo               #把foo的內存地址空間做值賦值給f變量
 5 print(f)            #打印f,起始就是打印foo的內存地址空間
 6 f()                 #f()事實上就是執行foo函數,等同於foo()
 7 
 8 
 9 打印結果為
10 <function foo at 0x0000000000B91378>    #print(foo)的結果
11 <function foo at 0x0000000000B91378>    #print(f)的結果
12 foo                                     #執行f()的結果,實際就是執行foo()的結果

2、函數可以當做參數傳遞

 1 def foo():
 2     print("foo")
 3 print(foo)          #打印foo的內存地址空間
 4 f=foo               #把foo的內存地址空間做值賦值給f變量
 5 print(f)            #打印f,起始就是打印foo的內存地址空間
 6 f()                 #f()事實上就是執行foo函數,等同於foo()
 7 
 8 def bar(func):
 9     print(func)     #這個是打印foo()函數的內存地址
10     func()          #func是foo的內存地址,加()實際就是執行foo()函數
11     return func
12 f = bar(foo)        #這個是獲取bar的返回值
13 print(f)            #打印f,就是打印bar()的返回值,就是foo()的內存地址,和print(func)相同
14 f()                 #f是bar
15 
16 def bar(func):
17     print(func)
18 
19 bar(foo())      #這個是先執行foo()行數,執行函數里的代碼,先打印foo,然后把foo()的返回值作為bar()函數的實參傳遞給bar,foo()沒有返回值,所以是None
20                 #結果就是先輸出一個  "foo"     然后bar(None),把none傳遞給bar()函數,打印No

 

 

四、閉包:內部函數的代碼包含對外部作用域的引用,但一定不是對全局作用域的引用,閉包函數一定有__closure__方法

 1 x=1
 2 def f1():
 3     x=2
 4     y=3
 5     def f2():
 6         print(x)
 7         y
 8     return f2
 9 f=f1()                  #f是f2的內存地址
10 f()                     #f()就是f2(),可以保證f2()可以在任何位置執行,而不受作用域的限制
11 print(f.__closure__)    #打印結果是元組,元組個數代表的是閉包所引用的上次函數的元素個數
12 print(f.__closure__[0]) #結果是元組,可以使用索引的方式查看
13 print(f.__closure__[0].cell_contents)   #查看指定元組所對應的應用上層參數的值
14 
15 def init(func):
16     def wrapper(*args,**kwargs):
17         res=func(*args,**kwargs)
18         return res
19     return wrapper
20     
21     

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM