函數的進階(命名空間和做用域)html
閉包java
♣一:if;else和縮進python
咱們先看看if判斷和循環能作什麼。閉包
在生產環境中,將會大量用到判斷循環,來將一個須要重複執行的操做簡化執行,若是沒有判斷循環,代碼就和記流水帳同樣麻煩,會加大人工時間成本,好比說我要打印1到100的數字,按照普通的方式打印print(1)........print(100),須要輸入100行相同的語句,會很麻煩,可是若是用循環判斷,簡單的幾行就能夠寫出來。app
打印0到20的數字:ide
#coding=gbk a = 1 #定義變量的初始值是1 b = 21 #定義打印數字的結束值21,爲何是21要看下的a < b判斷語句 for a in range(21): #for語句後面的range(21)在此是要for循環執行21次 if a < b: #1是否小於21,小於就執行下面的print語句 print(a, end=",") #上面的if條件知足就執行打印操做。 #執行結果 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,
在上述的循環判斷中,能夠看到咱們已經實現了0到20的數字打印,也基礎的瞭解到了循環判斷的用法,python的條件判斷是經過代碼塊裏面的運算符來控制輸出的內容,前面基礎篇(一)裏面咱們介紹了基礎的運算符,如今就能夠在循環判斷裏面進行實現重複的工做。函數
接下來咱們經過if的基本模式來認識if的執行過程。優化
如今有一個要求:評判整個班級的分數等級,1;大於50小於65的學生須要天天晚自習到9點,2;大於65小於85的晚自習到8點,3;大於85小於100的晚自習到7點, 4;其他的晚自習到10點。ui
#coding=gbk score = int(input("請輸入你的分數:\n")) #在用戶輸入的時候就須要指定輸入的類型,不然後面程序執行會報類型錯誤 if score >= 50 and score <= 65: #程序要求大於和小於某一個數字,可是在代碼裏面須要考慮到用戶輸入的數字是等於設定值的狀況下,怎麼處理 print("你須要晚自習到9點") #當用戶輸入數字了,那麼該數字會和下面的每一條判斷語句中的條件進行比對,知足指定的條件就會返回相應的結果 elif score >= 65 and score <= 85: print("你須要晚自習到8點") elif score >= 85 and score <= 100: #在每句判斷語句結束以後必需要加上冒號(:),這個也是最基本的語法要求,並且接下來的打印語句前面是帶空格的,這個也是python有別於其餘語言的地方,在java語言裏面可能看到判斷 print("你須要晚自習到7點") #語句後面是一對{花括號},這個是讓程序知道一段代碼的做用域就是靠{花括號}來定義的,這樣程序就知道這個判斷是要執行這個做用域下面的語句,而python就是靠縮進來定義做用域 else: #的,並且這個縮進標準就是4個空格,你們默認都是這麼遵照的,並且代碼越複雜,縮進就越多,這個也成了python的特色,強制縮進。 print("你須要晚自習到10點") #執行結果: 請輸入你的分數: #後面執行代碼若是出現IndentationError錯誤的,就是語法縮進有錯誤。 20 你須要晚自習到10點 請輸入你的分數: 90 你須要晚自習到7點
能夠經過下圖來簡單瞭解下語句執行的過程:url
注:
B:循環判斷:
上面的if判斷只能知足一次執行,通常在須要輸入密碼的地方都是須要限定次數的,若是超過這個次數限制可能就是有人在暴力破解,因此按照上面的代碼是不能實現的,永遠只能匹配到一次正確的,若是是本人不當心輸錯密碼,直接終止了程序,那還須要從新執行下程序,這個是及其不友好的,因此就須要咱們加上計數器這樣的代碼。
#coding=gbk y = 1 username = "克魯斯" password = 123123 while True: user = input("請輸入用戶名:\n") ps = int(input("請輸入密碼:\n")) if user == username and ps == password: print("歡迎回來,%s" %user) break else: y += 1 #經過y定義的初始值,當每次循環到這裏的時候就加1,而後再去執行下一次循環,當y > 3了,就執行break的操做 if y > 3: #經過計數器的功能,可讓循環可控。 print("密碼錯誤次數太多,請20秒以後在從新登陸") break #break是結束退出本次循環,還有一個continue是跳出本次循環,進行下一次循環 #exit() #和break的功能相似 執行結果:登錄成功 請輸入用戶名: 克魯斯 請輸入密碼: 1 請輸入用戶名: 克魯斯 請輸入密碼: 123123 歡迎回來,克魯斯 登錄失敗: 請輸入用戶名: 克魯斯 請輸入密碼: 1 請輸入用戶名: 克魯斯 請輸入密碼: 123123 歡迎回來,克魯斯
range()函數和len()函數常常會在一塊兒使用,range()函數用於快速生成一個有序的數字列,並且range只能在後面添加整數,len()函數用於返回列的索引,也就是前面說的下標,這兩個會常常一塊兒出現
一: #coding=gbk for i in range(10): print(i, end=",") #執行結果: 0,1,2,3,4,5,6,7,8,9, 二: for i in range(5,20): #從5開始到20結束 print(i, end=",") #執行結果: 5,6,7,8,9,10,11,12,13,14,15,16,17,18,19, 三: #coding=gbk for i in range(0,20 ,3): #從0開始到20結束,每隔3位數打印一個數 print(i, end=",") #執行結果: 0,3,6,9,12,15,18, #固然也能夠打印負數
#coding=gbk a = [1,2,3,4,5,6,7,8,9] for i in range(len(a)): #生成a列表中的下標參數 print(i,a[i]) #執行結果: 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9
renge還能夠用於數值列表的快速生成:
list(range(0,20,3)) #列表,調用函數range,從數字0開始到數字20結束,每隔3個數字取一個數字(含起始數字) #執行結果 [0,3,6,9,12,15,18,]
break的功能是退出for和while循環體,遇到else語句也不執行。
#coding=gbk for i in range(0,20): #從0開始到20結束 if i == 15: #當循環體裏面打印到15的時候 break #就結束整個循環體 print(i,end=",") #執行結果: 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,
continue的功能是用於跳出(跳過)本次的循環體,執行一次循環。
#coding=gbk for i in range(0,20): #從0開始到10結束 if i == 6 or i == 15 or i ==18 : #當循環體裏面打印到6,15和18的時候 continue #就跳出本次循環,執行下一次循環 print(i,end=",") #執行結果: 0,1,2,3,4,5,7,8,9,10,11,12,13,14,16,17,19, #能夠看到6,15,18就沒有打印
在代碼for和while循環的時候,同時使用了continue和break語句,在這種狀況下,continue要先於break執行,不然break會結束掉循環,continue可能就執行不到,除非是在break沒有觸發的狀況先使用continue和其餘特殊狀況。
#coding=gbk for i in range(0,20): if i == 6 or i == 15: continue elif i == 18: break print(i,end=",") #執行結果: 0,1,2,3,4,5,7,8,9,10,11,12,13,14,16,17,
pass沒有特殊的用意,通常用於代碼的完整性,主要就用於佔位語句,例如代碼的大部分已經完成,可是有些地方尚未想好怎麼寫,就可使用pass來佔據這個代碼位置,讓代碼先能夠執行,後續在優化的時候在回來把佔位的地方補上。
還有return,return主要是用於函數,做用也是終止的,後續說明,還有exit,退出。
♣二:for,while循環:
for和while最直接的區別就是,當明確循環次數的時候使用for,不清楚循環多少次的時候用while循環,(在實際開發過程當中for循環使用的概率大於while循環)
例:讓用戶輸入3個數,比對大小進行排序(這個裏面明確了次數3,就可使用for循環)
l = [] for i in range(3): x = int(raw_input('integer:\n')) l.append(x) l.sort() print l
1:首先要解決用戶輸入;
2:進行比對,這個可使用到排序sort()用法;
3:顯示用戶輸入程序排序過的數據。
♣三:函數基礎
上面咱們在完成一個需求寫代碼的時候仍是從上到下一步步完成,若是是出現需求調整,咱們就須要對代碼從上到下進行二次調整來知足需求,這樣就會出現一個問題就是代碼很不靈活,有時候需求調整一個很小的功能點就須要對代碼進行很大幅度的調整,還有就是下次遇到一個類似的需求,仍是要從新寫一遍的,這個時候就須要部分基礎代碼能被重複利用,這樣我就只須要調整其餘部分來知足需求,這個功能就是函數。
例如:我須要打印一個列表的長度,咱們可使用len()這個內置函數來完成,可是不使用len()的狀況下就須要使用for循環:
s = '初始化版本1' i = 0 for k in s: i += 1 #經過循環相加來完成計算 print(i) 若是是使用函數來完成: s = '初始化版本1' def my_s(): #使用關鍵字def後面接函數名(函數同變量定義方式同樣,後面的()括號是固定格式,括號裏面還能夠填寫內容。 i = 0 for k in s: i += 1 print(i) my_s() #函數的調用,直接使用函數名便可
上面是函數的基本使用方法,可是從上面的函數來看存在幾個問題,1;函數只能處理列表s,2;my_s()打印的方式不友好,若是是從可讀性來考慮的話,應該是pritn(i)這樣,3;無返回值。
咱們先看第三個問題,爲何須要返回值,有兩個列表,a和b,a列表長度爲3,b列表長度爲10,若是是要計算a+b列表的長度和,普通的print打印是不行的,這個時候須要將值暫存起來返回,而後經過print(a + b)來計算出和。
接下來在看第二個問題,my_s()若是是放在其餘代碼裏面執行,會不清楚my_s()打印的對象是誰。
s = '初始化版本1' def my_s(): #使用關鍵字def後面接函數名(函數同變量定義方式同樣,後面的()括號是固定格式,括號裏面還能夠填寫內容。 i = 0 for k in s: i += 1 return i #返回值的關鍵詞就是renurn s1 = my_s() print(s1)
經過返回值咱們的代碼可讀性變強了.
1:不寫renurn s = '初始化版本1' def my_s(): i = 0 for k in s: i += 1 s1 = my_s() print(s1) #執行結果: None 2:只寫return: s = '初始化版本1' def my_s(): i = 0 for k in s: i += 1 return #return後面不寫要返回的值的對象 s1 = my_s() print(s1) #執行結果: None 第一種的不寫return還能理解,可是第二種寫了return又不返回值,這個看起來是沒有意義的。這個時候須要說明到return的特殊用意,若是函數體裏面只要須要return,後面的代碼就不會繼續執行。 def my_s(): s = ['初始化版本1','版本2'] for i in s: print(i) return print('版本3') #會發現這行是沒有打印的,覺得這函數到return就結束了 s1 = my_s() print(s1) #執行結果: 初始化版本1 版本2 None 其實這個也是沒有意義的,可是我想執行到列表裏面的某一個元素就結束函數體,這個就會有意義了。 def my_s(): s = ['初始化版本1','版本2'] for i in s: print(i) if i == '初始化版本1': #遇到指定元素就結束函數體,下一個元素就不會打印了 return s1 = my_s() print(s1) #執行結果: 初始化版本1 None def my_s(): s = ['初始化版本1','版本2'] for i in s: print(i) if i == '初始化版本1': #遇到指定元素就結束函數體,下一個元素就不會打印了 break #以前咱們也學過break跳脫,可是這個跳脫只是跳出for這個循環,跳不出函數體,函數下面還有代碼的狀況下照樣打印 print('@'*10) s1 = my_s() print(s1) #執行結果: 初始化版本1 @@@@@@@@@@ None def my_s(): s = ['初始化版本1','版本2'] for i in s: print(i) if i == '初始化版本1': #遇到指定元素就結束函數體,下一個元素就不會打印了 return #return是結束一個函數 print('@'*10) s1 = my_s() print(s1) #執行結果: 初始化版本1 None
def my_k(): return [1,2,3,4] k = my_k() #以前的狀況都是讓函數賦值給一個變量在print print(k) #執行結果 [1, 2, 3, 4] 其實是不須要這麼麻煩的,能夠直接打印函數名就能夠 def my_k(): return [1,2,3,4] print(my_k()) #執行結果 [1, 2, 3, 4] 從上面的條件能看出 1:return能夠返回任何數據 2:只要return返回了就能夠接受到 3:在返回一個值的狀況下也是和以前同樣,若是函數裏面有多個return,只執行第一個,後面都不執行
def my_k(): return 1,2,3 #有三個返回值的狀況 k1,k2 = my_k() print(k1,k2) #執行結果 Traceback (most recent call last): File "/Users/yangpengcheng/Documents/python_file/mg_函數基礎.py", line 33, in <module> k1,k2 = my_k() ValueError: too many values to unpack (expected 2) 執行結果報錯。 def my_k(): return 1,2,3 #有三個返回值的狀況 print(my_k()) #執行結果 #(1, 2, 3) #獲得的是一個元祖,這個結果就可使用元祖的方法來操做了 調整函數: def my_k(): return 1,2,3 #有三個返回值的狀況 k1 = my_k() print(k1) #執行結果 (1, 2, 3) #獲得的是一個元祖,這個結果就可使用元祖的方法來操做了 def my_k(): return 1,2,3 #有三個返回值的狀況 k1,k2,k3 = my_k() print(k1,k2,k3) #執行結果 1 2 3 #和預期結果相符 返回值3個,打印兩個的狀況 def my_k(): return 1,2,3 #有三個返回值的狀況 k1,k2 = my_k() print(k1,k2) #執行結果 Traceback (most recent call last): File "/Users/yangpengcheng/Documents/python_file/mg_函數基礎.py", line 33, in <module> k1,k2 = my_k() ValueError: too many values to unpack (expected 2) 執行結果報錯。 從上面得出兩個結論: 1:接受多個返回值的時候,返回值有多少個就須要多少個變量來接受,不能多也不能少 2:若是是使用一個變量來接受或者直接print打印的狀況下獲得的結果是一個元祖,這個是python內部幫忙進行的轉換。
優化上述函數只能處理列表和計算和的問題:
def my_k1(k): #經過函數名括號裏面標記接受參數,可是後面函數調用的時候傳參並不知道是以什麼形式體現的,因此這個地方的標記接受參數就只是一個形式上的參數,簡稱形參 i = 0 for k1 in k: i += 1 return i k2 = my_k1("初始化版本1") #經過函數名來傳遞參數,由函數體接受傳遞進去的參數來執行,這個傳遞的參數是真正的取用的實際參數,簡稱實參 k3 = my_k1([1,2,3,4,5,6,7]) print(k2,k3) print(k2 + k3) #執行結果 6 7 13
返回多個值的位置參數和關鍵字參數:
def my_sum(a,b): #在接收參數的位置咱們接收多個參數 my1 = a + b return my1 my2 = my_sum('版本1','版本2')#在傳遞參數的時候也須要針對上面接收參數的位數給足參數 my3 = my_sum(1,99) print(my2) print(my3) 執行結果: 版本1版本2 100 def my_sum(a,b): #在接收參數的位置咱們接收多個參數,這個接收多個參數的形式叫位置參數 my1 = a + b return my1 # my2 = my_sum('版本1','版本2')#在傳遞參數的時候也須要針對上面接收參數的位數給足參數 # my3 = my_sum(1,99) my4 = my_sum(2) #傳遞參數的位置若是是給了一個參數,就會報錯 # print(my2) # print(my3) print(my4) #執行結果: Traceback (most recent call last): File "/Users/yangpengcheng/Documents/python_file/mg_函數基礎.py", line 100, in <module> my4 = my_sum(2) TypeError: my_sum() missing 1 required positional argument: 'b' 執行報錯 def my_sum(a,b): print(a,b) my1 = a + b return my1 my2 = my_sum(99,1) my3 = my_sum(b = 1,a = 99) #也能夠經過關鍵字傳遞參數 my4 = my_sum(1,b = 99) #也能夠混合使用,可是必須先進行位置傳參,在進行關鍵字傳參數,不得將多個實參指向同一個位置形參。 print(my2,my3,my4) #執行結果: 99 1 99 1 1 99 100 100 100
返回多個值的默認參數:
def classmate(name,sex='男'): #當參數裏面出現一個頻繁出現的參數時,就可使用默認參數,默認參數能夠不傳,若是傳了就用傳遞的參數,並且這個默認參數其實就是上面的關鍵字參數 print('%s : %s'%(name,sex)) classmate('金吒') classmate('哪吒') classmate('木吒','女') # 執行結果: # 金吒 : 男 # 哪吒 : 男 # 木吒 : 女
返回多個值的動態參數:def sum(*kk): #動態參數定義的關鍵是*,後面是什麼無所謂,可是在實際工做中動態參數是*後面接args,這個是習慣問題。
def sum(*args) k = 0 for i in kk: k += i return k print(sum(1,2,3,4))#這樣計算數據我就不須要考慮事先留下位置或者默認值,有多少處理多少。 print(sum(67,234)) 執行結果: 10 301 def sum(*kk,num='12'): #在動態參數後面在接一個關鍵字參數。 k = 0 for i in kk: k += i return k print(sum(1,2,3,4,num=13)) print(sum(67,234)) 執行結果: 10 #發現關鍵字參數並無被執行,這個也就說明,動態參數是不能處理關鍵字參數的 301 可是我就想執行上面的關鍵字參數,可使用**來完成,**後面也有一個約定俗成的詞kwargs def fum(**kwargs): #**就能夠處理任何狀況下參數 print(kwargs) fum(a = 1,b = 3,c = 5) fum(b = 100,c = 200) #注,此處傳遞的參數仍是須要遵循變量的命名方式來,即不能是數字開頭 # 執行結果: {'a': 1, 'b': 3, 'c': 5} {'b': 100, 'c': 200} 動態參數: #*args :接收的是按照位置傳參的值,組織成一個元祖 #**kwargs :接收是按照關鍵字傳參的值,組織成一個字典 def fum(*args,**kwargs): print(args,kwargs) fum(1,2,3,4,5,a = "kkk",b = "版本1") # 執行結果: (1, 2, 3, 4, 5) {'a': 'kkk', 'b': '版本1'} #必須是args在前,kwargs在後面
經過上述的例子,能夠總結出來函數特性:
def fun(*args): print(args) fun(1,2,3,4,5) k = [1,2,3,4,5] fun(k[0],k[1],k[2]) 執行結果: (1, 2, 3, 4, 5) (1, 2, 3) #取元祖的下表能夠把參數打印出來,可是若是是有100個參數,這個方法有點不適用了。 def fun(*args): print(args) fun(1,2,3,4,5) k = [1,2,3,4,5] fun(*k) #能夠在變量名前面加一個*號,就會按照順序來打散 執行結果: (1, 2, 3, 4, 5) (1, 2, 3, 4, 5) def fun(**kwargs): print(kwargs) fun(a=1,b=2) k = {'a':1,'b':2} fun(**k) #一樣的**也能夠完成同樣的需求 #執行結果 {'a': 1, 'b': 2} {'a': 1, 'b': 2}
def ll(l = []): l.append(1) print(l) ll() ll([]) ll() def aa(k,k1 = {}):#這兩個函數存在一個共同點就是數據類型都是一個可變的類型。 k1[k] = 'v' #字典和列表都是能夠編輯的 print(k1) aa(1) aa(2) aa(3) #執行結果 [1] [1] #這個執行方式是11([]),函數形參是默認參數形式,當默認參數有值就用默認的值,我傳了值就用傳的值 [1, 1] {1: 'v'} {1: 'v', 2: 'v'} {1: 'v', 2: 'v', 3: 'v'} #經過上述結果咱們得出,若是默認參數是一個可變類型的數據,若是不給它傳值,他就始終公用同一個數據類型的資源
def fun(): ''' 函數的功能 參數1 參數2 :return:返回的是什麼類型的數據,下面在開始寫你的函數體 '''
此類空間是在python解釋器啓動的時候就加在到內存空間中,這個空間裏面函數名就是例如print,inport等,咱們能夠隨時調用。
在內置裏面是不能使用全局和局部命名空間中的名字,由於內置是python啓動的時候加載進來的,後面的空間都是解釋器加載結束後再逐一加載的,除非你能把你的命名加到python啓動的過程當中。
此類空間是咱們程序在執行的過程當中從上到下逐一加到內存空間中,這個空間裏面存的命名實際上是咱們設置的變量,函數名等。
在全局裏面可使用內置的命名空間中的名字,但不能使用局部的,由於局部執行結束就會釋放。
此類命名是咱們函數內部定義的名字,當函數調用的時候纔會產生這個空間,這個函數執行結束了,開闢的內存空間也就隨之釋放掉了。
在局部裏面可使用全局,內置命名空間中的名字。
在命名空間裏面永遠是內置<全局<局部,可是當咱們全局定義了內置空間中的名字,會使用全局的名字,一樣,在局部使用了全局和內置空間中的名字,會使用局部空間裏面的名字
同理,當局部一段代碼執行,程序先去局部找,局部找不到再去全局,最後去內置找,都找不到就報錯,可是全局和內置是不能反過來到局部裏面去找名字的。
在局部空間函數名是隔離開來的,即便兩個函數使用了相同的命名,二者不相互影響
在內置和全局命名空間的名字都屬於全局做用域
函數(局部命名空間的名字屬於局部做用域)
a = 1 def k1(): a += 1 #在全局已經定義,在局部直接去修改是不能夠的,此類數據對於局部來講是不可變的類型 def k2(): global a #若是要想直接調整全局做用域的數據,須要經過global來聲明,並且在局部經過global聲明瞭變量,那麼這個變量在局部的操做也會對全局有效 a += 1 k2() print(a) #執行結果 2 #在實際場景中global是儘可能少用,由於他們會調整全局變量的值,這樣會致使全局變量被改變出現bug。 b = 1 c = 2 def k3(): x = 'bbb' y = 'ccc' print(locals()) #使用locals()能夠查看到我局部空間的全部名字 k3() print(globals()) #使用glbals()能夠查看到全局和內置空間中的名字,glbals()只能看全局和內置,不能看局部的 print(locals()) #把locals()放到全局也能夠查看到全局和內置的名字,這個locals()查看的範圍取決於locals()所在的位置 #執行結果 {'y': 'ccc', 'x': 'bbb'} {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x101f5a3c8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/Users/yangpengcheng/Documents/python_file/mg_函數的進階.py', '__cached__': None, 'a': 2, 'k1': <function k1 at 0x101bcaea0>, 'k2': <function k2 at 0x1041c6510>, 'b': 1, 'c': 2, 'k3': <function k3 at 0x1041c6620>} {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x10405a3c8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/Users/yangpengcheng/Documents/python_file/mg_函數的進階.py', '__cached__': None, 'a': 2, 'k1': <function k1 at 0x101bcaea0>, 'k2': <function k2 at 0x1041a6510>, 'b': 1, 'c': 2, 'k3': <function k3 at 0x1041a6620>}
def max(a,b): return a if a>b else b #定義比大小函數1 def max1(x,y,z): c = max(x,y) #在第二個函數裏面我直接使用函數1的功能,有相同的需求,能夠重複使用,減小工做量 return max(c,z) print(max1(3,9,2)) #執行結果 9 #上述的過程就是函數的嵌套調用。
a = 1 def k1(): a = 1 def k2(): b = 2 nonlocal a #若是是要調整局部變量的值,py3裏面提供了nonlocal,它能夠調整函數局部變量的值,可是它的邏輯是在函數體裏面往上找,離nonlocal最近的變量的值將被改變,使用global只能改全局的變量值 a += 1 print(a) print('k2') k2() k1() print(a) 執行結果 1 k2 1 取消nonlocal註釋: 2 k2 1
函數名實質就是一個內存地址,一個內存地址是能夠被多個變量名,函數名指向的,這樣咱們就可使用這個內存地址點對多指向特性完成不少複雜的事情。
def sum1(): print(123) sum2 = sum1 #1:函數名是能夠賦值給其餘變量名的,而且兩個函數指向了同一個變量值。 sum2() sum3 = [sum1,sum2] #2:函數名能夠做爲容器類型(列表,元祖等)的元素 print(sum3) #能夠看到sum2和sum1指向的是一個內存地址 for i in sum3: i() #能夠看到函數執行的結果是同樣的 # 執行結果: # 123 # [<function sum1 at 0x000001463FD041E0>, <function sum1 at 0x000001463FD041E0>] # 123 # 123 def sum2(sum3): sum3() sum2(sum1) #3:函數名能夠做爲函數的參數 # 執行結果: # 123 def sum2(sum3): sum3() return sum3 #4:函數名能夠做爲函數的返回值 sum4 = sum2(sum1) sum4() # 執行結果 # 123 # 123
只要知足如下幾種類型的都統稱爲第一類對象:
第一類對象能夠理解爲普通變量,用法類似,惟獨函數能夠函數名()這樣去執行
閉包的必定是嵌套函數機構,內部函數調用外部函數的變量 def sum1(): a = 1 def sum2(): print(a) print(sum2.__closure__) sum1() print(sum1.__closure__) # 執行結果: # (<cell at 0x000002BA196AA618: int object at 0x000000006493BC10>,) #若是執行結果裏面有cell at字樣就說明是閉包 # None 直接打印是空的就不是閉包 def sum1(): a = 1 def sum2(): print(1) #此處的1是沒有定義的,能夠看到結果是空,空就不是閉包 print(sum2.__closure__) sum1() # 執行結果 # None
def sum1(): a = 1 def sum2(): print(1) return sum2 sum3 = sum1() sum3() #閉包的最經常使用方法就是外部使用內部的函數變量 # 執行結果 # 1
爲何要使用閉包,若是沒有閉包的狀況下,咱們外部用內部的函數值,執行的過程當中是要在內存裏面開啓內存空間的,並且是每次執行都要單獨再開啓,函數使用完畢內存就釋放了,因此爲了執行效率,解決辦法就是讓這個內存空間延續,
不要釋放,閉包目的就在於此。
import urllib #導入urllib函數 from urllib.request import urlopen ret = urlopen('https://www.autohome.com.cn/beijing/').read() #使用urllib模塊用法打開一個網頁 print(ret) 上面咱們打開網頁,若是是想爬取頁面上的內容,好比是汽車之家上面的汽車的圖片,那麼確定是要拿到網頁源碼,拿到以後進行篩選,關鍵字是jpg結尾的圖片,這樣的狀況下就不能讓網頁來回加載(重複生成變量),效率會很低。 def get_url(): url = 'https://www.autohome.com.cn/beijing/' def get(): ret = urlopen(url).read() print(ret) return get get_open = get_url() get_open() 執行結果 b'\r\n\r\n<!DOCTYPE html>\r\n\r\n<html>\r\n<head>\r\n html網頁源碼內容,和你網頁右鍵查看源代碼顯示的內容同樣