python-函数进阶
? python-函數進階
1,名稱空間
又名name space, 顧名思義就是存放名字的地方,存什么名字呢?舉例說明,若變量x=1,1存放于內存中,那名字x存放在哪里呢?名稱空間正是存放名字x與1綁定關系的地方
名稱空間共3種,分別如下
- locals: 是函數內的名稱空間,包括局部變量和形參
- globals: 全局變量,函數定義所在模塊的名字空間
- builtins: 內置模塊的名字空間,通過dir(_builtins_)查看..
2,作用域的查找順序
不同變量的作用域不同就是由這個變量所在的命名空間決定的
作用域即范圍
全局范圍:全局存活,全局有效
局部范圍:臨時存活,局部有效
查看作用域方法 globals(), locals()
作用域的查找順序
LEGB 代表名字查找順序: locals -> enclosing function -> globals -> __builtins__
- locals 是函數內的名字空間,包括局部變量和形參
- enclosing 外部嵌套函數的名字空間
- globals 全局變量,函數定義所在模塊的名字空間
- builtins 內置模塊的名字空間
3,for本質
優點(和其它循環(while)相比的):比whlle等循環功能強大,不僅遍歷的對象種類多,而且比普通循環效率更高(自動把遍歷對象生成迭代器)
定義:遍歷可迭代對象(string,list,dict等),如果是遍歷可迭代對象,for會自動把in后面的可迭代對象轉換成迭代器,把所有元素依次加載到內存,遍歷完成后自動處理異常
1 for i in t: # t被for轉換成t.__iter__() 2 print(i)等效于
1 t = [1, 2, 3, 4, 5].__iter__() # 可以通過__iter__()方法或iter()內建函數把Iterable類型變成Iterator對象。 2 # 循環: 3 while True: 4 try: 5 # 獲得下一個值: 6 i = t.next() 7 print(i) 8 except StopIteration: 9 # 遇到StopIteration就退出循環 10 break
4,閉包
內部函數對外部函數作用域里變量的引用(非全局變量),則稱內部函數為閉包。
說明:閉包指的是內層函數,之所以叫閉包,閉是因為在外層函數內,包是因為和外層函數的變量綁定在一起。
1.閉包的定義 # 結構:兩個def,兩個變量,返回內存地址 2.考慮三部:1,定義的函數是否是閉包2,看他怎么用(執行外層函數賦給一個變量,后面執行這個變量會導致這個這個不會銷毀 )3,產生bug,內層波函數修改外層函數變量, 閉包定義 1 def outer(): 2 n = 10 3 4 def inner(): 5 print("inner:", n) 6 return inner 7 8 val = outer() 9 print(val) 10 val() 11 12 # 執行結果<br><function outer.<locals>.inner at 0x0033A390> 13 inner: 10閉包的意義:返回的函數對象,不僅僅是一個函數對象,在該函數外還包裹了一層作用域,這使得,該函數無論在何處調用,優先使用自己外層包裹的作用域
保存上次的運行環境(外層函數的的生命周期結束之后,外層函數的變量不被銷毀)
1 def outer(name): 2 count = [0] 3 4 def inner(): 5 count[0] += 1 6 print('Hello,', name, ', ', str(count[0]) + ' access!') 7 print(count) 8 9 return inner 10 11 12 hello = outer('hy') 13 hello() 14 hello() 15 hello() 16 17 ### 18 Hello, hy , 1 access! 19 [1] 20 Hello, hy , 2 access! 21 [2] 22 Hello, hy , 3 access! 23 [3] 這里面調用outer的時候就產生了一個閉包——inner,并且該閉包持有自由變量——count,因此這也意味著,當函數outer的生命周期結束之后,count這個變量依然存在,因為它被閉包引用了,所以不會被回收。
另外再說一點,閉包并不是Python中特有的概念,所有把函數做為一等公民的語言均有閉包的概念。不過像Java這樣以class為一等公民的語言中也可以使用閉包,只是它得用類或接口來實現。
閉包思考:
1.閉包似優化了變量,原來需要類對象完成的工作,閉包也可以完成
2.由于閉包引用了外部函數的局部變量,則外部函數的局部變量沒有及時釋放,消耗內存
自由變量
1,在閉包中,被調用并改動,引發bug的那個值,是自由變量。
2,在裝飾器中,被傳進來的,但是未引發bug的那個值,是自由變量。(如裝飾器的局部變量被嵌套函數調用并修改,從而引發bug,這個局部變量就是自由變量,從意義上來講,這個局部變量被引用并改動,導致其不被釋放,就會使局部變量升華成類似全局變量)。
比如閉包中這個被傳進去的name,被閉包調用并且沒有改變,所以就是自由變量。裝飾器里面,count被引用并修改了,形成了bug,導致count不被銷毀,升華成類似全局變量,它也是自由變量。
?5,裝飾器?
裝飾器 = 高階函數 + 函數嵌套 + 閉包
裝飾器的定義:特殊的閉包1.自由變量是函數2.外層函數執行后賦給的變量名和使用裝飾器的函數名相同,所以可以不改變用法。3.函數傳到閉包中,函數并沒有被修改,因此不會產生BUG) 裝飾器定義1,高階函數
變量可以指向函數,函數的參數能接收變量,那么一個函數就可以接收另一個函數作為參數,這種函數就稱之為高階函數。
編寫高階函數,就是讓函數的參數能夠接收別的函數。
只需滿足以下任意一個條件,即是高階函數
接受一個或多個函數作為輸入
return 返回另外一個函數本身
1 # 接受一個或多個函數作為輸入 2 def add(x, y, f): 3 return f(x)+f(y) 4 val = add(5, 6, abs) # 參數可以接受函數 5 print(val) 6 7 # return返回另外一個函數本身 8 def calc(x, y): 9 return x+y 10 def add2(): 11 return calc # return可以返回另一個函數 12 val2 = add2() 13 print(val2(3, 6))2,嵌套函數
用于先準備一個函數,即外層函數執行完后,內層函數仍可以使用外層函數的變量,裝飾器應用了嵌套函數。
1 def line_conf(a, b): 2 def line(x): # 參數其實傳到了這里來了 3 return a * x + b 4 5 return line 6 7 line1 = line_conf(1, 1) 8 line2 = line_conf(4, 5) 9 print(line1(5)) 10 print(line2(5))<br><br>###<br>6<br>25通俗一點:執行的時候,line1=line_conf,帶入函數,返回的是line,所以此時line1=line,變量a,b已經傳入進去了,后面執行line1(5)的時候,其實就是執行第二行的line(x)。
這個例子中,函數line與環境變量a,b構 成閉包。在創建閉包的時候,我們通過line_conf的參數a,b說明了這兩個環境變量的取值,這樣,我們就確定了函數的最終形式(y = x + 1和y = 4x + 5)。我們只需要變換參數a,b,就可以獲得不同的直線表達函數。由此,我們可以看到,利用閉包,使開始賦予的值沒有銷毀,閉包也具有提高代碼可復用性的作用。缺點消耗了內存,沒有釋放出來。
如果沒有閉包,我們需要每次創建直線函數的時候同時說明a,b,x。這樣,我們就需要更多的參數傳遞,也減少了代碼的可移植性。利用閉包,我們實際上創建了泛函。line函數定義一種廣泛意義的函數。這個函數的一些方面已經確定(必須是直線),但另一些方面(比如a和b參數待定)。隨后,我們根據line_conf傳遞來的參數,通過閉包的形式,將最終函數確定下來。
?
嵌套函數和閉包的聯系:
你可以在一個函數里面嵌套另外一個函數。嵌套(內部)函數對其容器(外部)函數是私有的。它自身也形成了一個閉包。一個閉包是一個可以自己擁有獨立的環境與變量的的表達式(通常是函數)。
既然嵌套函數是一個閉包,就意味著一個嵌套函數可以”繼承“容器函數的參數和變量。換句話說,內部函數包含外部函數的作用域。
可以總結如下:
- 內部函數只可以在外部函數中訪問。
- 內部函數形成了一個閉包:它可以訪問外部函數的參數和變量,但是外部函數卻不能使用它的參數和變量。
由于內部函數形成了閉包,因此你可以調用外部函數并為外部函數和內部函數指定參數
3,裝飾器
裝飾函數
定義:裝飾器是特殊的閉包(1,自由變量是函數。2,外層函數執行后賦給的變量名和使用裝飾器的函數名相同,所以可以不改變用法。3,函數傳到閉包中,函數并沒有被修改,因此不會產生BUG)
?
作用:不改動原函數、不改變原函數調用方式的前提下,擴展函數功能,遵循了開放封閉原則
需求一:為源碼函數拓展功能,且不改變調用方式
# _*_ encoding:utf-8 _*_def login(func):print("passer user vertification...")func()def tv():print("Welcome [%s] to home page")tv = login(tv) # tv的值為Nonetv() # 這步出錯:TypeError: 'NoneType' object is not callable# 輸出 passer user vertification... File "D:/PythonProject/0313/str.py", line 14, in <module> tv() TypeError: 'NoneType' object is not callable問題: 1.調用方法改變了 2.調用端還沒有調用 tv(),擴展的功能就先執行了 v1 def login(func):print("passer user vertification...")return func@login #@login等效于tv = login(tv) def tv():print("Welcome [%s] to tv page")tv()# 輸出 # passer user vertification... # Welcome [%s] to tv page # # 問題: # 調用端還沒有調用 tv(),擴展的功能就執行執行 v2 def login(func):print("passer user vertification...")return func@login #@login等效于tv = login(tv) def tv():print("Welcome [%s] to tv page")tv()# 輸出 # passer user vertification... # Welcome [%s] to tv page # # 問題: # 調用端還沒有調用 tv(),擴展的功能就執行執行 v2.1 def login(func):def inner():print("passed user vertification...")func()return innerdef home():print("Welcome [%s] to home page" % name)# @login # tv=login(tv)def tv():print("Welcome [%s] to tv page")def movie():print("Welcome [%s] to home movie" % name)tv=login(tv)tv() v3.1 def outer(func): def inner(args): print("passed user vertification...")func(args)return inner@outer # 這個@outer相當于將outer下面的函數當outer的參數執行,也即,tv = outer(tv) def tv(name):print("Welcome [%s] to tv page" % name)tv('hy')### passed user vertification... Welcome [hy] to tv page v3.2
需求二:需求一 + 傳遞參數 + 返回值
需求三:需求二 + 含參裝飾器
def wrapper(*wrapperargs):def outer(func): # func=tvdef inner(*args, **kwargs): # 參數其實傳到了這里print("passed user vertification...")print '[%s] looks like very [%s]' % (''.join(args), ''.join(wrapperargs))return func(*args, **kwargs)return innerreturn outer@wrapper('shuai') # tv=outer(tv),tv=inner,tv()=inner() def tv(name):print("Welcome [%s] to tv page" % name)return 6print tv('hy')######## passed user vertification... [hy] looks like very [shuai] Welcome [hy] to tv page 6 func有返回值+func動態參數+裝飾器動態參數 def Before():print('before')def After():print('after')def wrapper(before_func, after_func):def outer(func): # func=tvdef inner(*args, **kwargs): # 參數其實傳到了這里print("passed user vertification...")before_func()after_func()return func(*args, **kwargs)return innerreturn outer@wrapper(Before, After) def tv(name):print("Welcome [%s] to tv page" % name)return 6print(tv('hy'))####### passed user vertification... before after Welcome [hy] to tv pagefunc有返回值+func動態參數+裝飾器參數(函數作為參數) func有返回值+func動態參數+裝飾器參數(函數作為參數)需求四:多層裝飾器
def outer(func): def inner(*args,**kwargs): print('log') r=func(*args,**kwargs) print('after') return r return inner def outer2(func): def inner(*args,**kwargs): if LOGIN_INFO['is_login']: r=func() return r else: print('please login') return inner #如果套兩層裝飾器,就是雙層裝飾器了,當然也有三層,四層,道理類似 #這里大家可能有疑惑,python在解釋有 “@+函數”這種格式的語法時,會自動從里向外解讀,再從外向內執行, #也就是最里層的原函數被逐層裝飾直到最外層,對應例子里,python先把f2(原函數)發給outer2(里層裝飾器),被裝飾后的outer2的inner再 #被outer(外層裝飾器)裝飾,最終返回的是outer的inner函數體。 @outer @outer2 def f2(a,v): print('F2') #當然有人問主函數的調用為啥這樣寫呢,這個會在模塊對于的blog中介紹 if __name__ == '__main__': f2 隨意組合上面幾種類型裝飾器4,類裝飾器
裝飾函數
?再來看看類裝飾器,相比函數裝飾器,類裝飾器具有靈活度大、高內聚、封裝性等優點。使用類裝飾器還可以依靠類內部的__call__方法,當使用 @ 形式將裝飾器附加到函數上時,就會調用此方法。
class Outer(object):def __init__(self, func):self._func = funcdef __call__(self):self._func()@Outer # tv=Outer(tv) def tv():print("Welcome to tv page")tv()### Welcome to tv page 無參數 + 無返回值6,生成式
1,列表生成器(推導式)
python一種獨特的語法,相當于語法糖的存在,可以幫你在某些場合寫出比較精簡炫酷的代碼,帶沒有它,也不會有太多的影響。
語法糖指計算機語言中添加的某種語法,這種語法對語言的功能并沒有影響,但是更方便程序員使用。通常來說使用語法糖能夠增加程序的可讀性,從而減少程序代碼出錯的機會。
需求:把a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]里面的每個元素+1
1 # sb版本 2 >>> b = [] 3 >>> for i in a:b.append(i+1) 4 >>> a = b 5 6 # 普通版本 7 a = [1,3,4,6,7,7,8,9,11] 8 9 for index,i in enumerate(a): 10 a[index] +=1 11 print(a) 12 13 # 文藝版本 14 >>> a = map(lambda x:x+1,a) 15 >>> a 16 <map object at 0x02861810> 17 >>> list(a) 18 [2, 3, 4, 5, 6, 7, 8, 9]裝逼版本(列表生成式版本)
1 >>> a = [i+1 for i in range(10)] 2 >>> a 3 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]<br><br>>>>a = [i for i in range(5) if i % 2 == 0 and not i == 2]<br>>>>print(a)<br>[0,4]加入三元運算式
1 # 三元運算加入 2 a = list(range(10)) 3 c = [i if 8 < 5 else i+1 for i in a] <br># 每次都會先執行for i in a,然后再循環前面的部分,每循環一次,前面都會執行一次,挨個得出相應的值并存放 4 # a可以循環字典,元組,甚至字符串<br>print(c) 5 6 # 執行結果 7 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # 看下面代碼回答輸出的結果是什么?為什么? result = [lambda x: x + i for i in range(10)] print(result[0](10))這是一個結合了變量作用域、列表推導式和匿名函數的題目,較為復雜,比較考驗Python基礎知識掌握程度。有同學可能會回答10,其實答案是19,并且result[0~9](10)的結果都是19。這是因為函數具有調用時才查找變量的特性。在你沒調用它之前,它不會保存也不關心它內部變量的具體值。只有等到你調用它的時候,它才逐一去找這些變量的具體值。這里的result[0]被調用的時候,變量i已經循環完畢,變成9了,而不是想象中的動態0-9值。那如果不想要這樣的結果,想要i是循環的值怎么辦?不要直接引用上層變量,把變量直接傳進來。result = [lambda x, i=i: x + i for i in range(10)] print(result[0](10)) 面試真題2,生成器
作用:
1.逐步生成序列,不用像列表一樣初始化時就要開辟所有的空間(相當于python2.x的xrange)
2.模擬并發:協程(Python實現協程最簡單的方法,就是使用yield)
定義:如果函數中包含yield語法,那這個函數就會變成生成器,這個函數調用時返回一個迭代器,生成器屬于迭代器
通過列表生成式,我們可以直接創建一個列表。但是,受到內存限制,列表容量肯定是有限的。而且,創建一個包含100萬個元素的列表,不僅占用很大的存儲空間,如果我們僅僅需要訪問前面幾個元素,那后面絕大多數元素占用的空間都白白浪費了。
所以,如果列表元素可以按照某種算法推算出來,那我們是否可以在循環的過程中不斷推算出后續的元素呢?這樣就不必創建完整的list,從而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,稱為生成器:generator。
生成器也可以理解為(迭代器對象返回的是函數;特殊的函數;特殊的迭代器(遍歷無序數據結構),而特殊在迭代器對象是一個函數,不是列表,字符串(數字)的集合對象。)
要創建一個generator,有很多種方法。第一種方法很簡單,只要把一個列表生成式的[]改成(),就創建了一個generator:
1 >>> L = [x * x for x in range(10)] 2 >>> L 3 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 4 >>> g = (x * x for x in range(10)) 5 >>> g 6 <generator object <genexpr> at 0x1022ef630>? ? L是一個list,g是一個generator,如果要一個一個打印出來,使用next()函數獲得generator的下一個返回值。?
特性:1,取一次創建一次。2,只能往前走,不能回退。
1 >>> next(g) 2 0 3 >>> next(g) 4 1 5 >>> next(g) 6 4 7 >>> next(g) 8 9使用while執行完以后也會報錯。
a = list(range(5)) while True:next(a)# 執行結果:正常的執行完以后還是會報錯。生成器循環最好使用for來進行。
使用next,while生產完后會報錯,但是用for循環不會報錯,而generator也是可迭代對象:
1 >>> g = (x * x for x in range(4)) 2 >>> for n in g: 3 ... print(n) 4 ... 5 0 6 1 7 4 8 9斐波那契數列:除第一個和第二個數外,任意一個數都可由前面兩個數相加得到。
1, 1, 2, 3, 5, 8, 13, 21...
1 def fib(max): 2 n, a, b = 0, 0, 1 3 while n < max: 4 print(b) 5 a, b = b, a + b # 相當于 t = a + b, a = b, b = t 6 n = n + 1 7 return 'done'<br>fib(3)執行結果:1,1,2,3
? 利用yield改為生成器
1 def fib(max): 2 n, a, b = 0, 0, 1 3 while n < max: 4 print('before yield') 5 yield b # 把函數的執行過程凍結在這一步,并且把b的值返回給外面的next() 6 print(b) 7 a, b = b, a+b 8 n = n+1 9 return "done" 10 f = fib(15) # turn function into a generator 11 next(f) # fitst time call next() 12 next(f) 13 next(f) 14 next(f) 15 next(f) 16 17 # 執行結果 18 before yield 19 1 20 before yield 21 1 22 before yield 23 2 24 before yield 25 3 26 before yield在函數執行中,有些數據想返回到外部程序里,可以使用yield
? 里邊有yield,函數名一加括號,內部代碼根本不執行,只是生成一個生成器對象,
3,生成器調用方法
在python2中,range = list, xrange = 生成器
在python3中,range = 生成器, xrange 沒有
4,函數生成器
支持更復雜的步驟,可以使用函數生成器。
1 def range2(n): 2 conut = 0 3 while conut < n: 4 print('count', count) 5 count += 1 6 yield count 7 new_range = range2(10) 8 r1 = next(new_range) 9 print(r1) 10 r2 = next(new_range) # 相等于 r2 = new_range.__next__ 11 print(r2)yield vs return
return 返回并中止函數
yield 返回 數據,并凍結當前的執行過程
next 喚醒凍結的函數執行過程,繼續執行,直到遇到下一個yield
函數有了yield之后
函數名加()就變得到了一個生成器。
return在生成器里,代表生成器的中止,直接報錯。
5,生成器send方法
sent作用:
喚醒并繼續執行
發送一個信息到生成器內部
1 def range2(n): 2 conut = 0 3 while conut < n: 4 print('count', count) 5 count += 1 6 sign = yield count 7 if sign == 'stop': 8 print('----sign',sign) 9 break 10 new_range = range2(3) 11 n1= next(new_range) 12 13 new_range.send('stop') 14 # 執行結果 15 count 0 16 ----sign stop 17 count 16,協程函數
python中yield淺析
利用yield將函數變成一個generater(生成器),整個函數變成一個generater對象,函數返回一個iterable對象(迭代值)
當一個函數在執行過程中被阻塞時,就用yield掛起,然后執行另一個函數。當阻塞結束后,可以用next()或者send()喚醒。相比多線程,協程的好處是它在一個線程內執行,避免線程之間切換帶來的額外開銷,而且多線程中使用共享資源,往往需要加鎖,而協程不需要,因為代碼的執行順序是你完全可以預見的,不存在多個線程同時寫某個共享變量而導致出錯的情況。
1 #如果在一個函數內部yield的使用方式是表達式形式的話,如x=yield,那么該函數成為協程函數 2 def eater(name): 3 print('%s start to eat food' % name) 4 food_list = [] 5 while True: 6 food = yield food_list 7 print('%s get %s ,to start eat' % (name, food)) 8 food_list.append(food) 9 10 print('done') 11 12 e = eater('鋼蛋') 13 # print(e) 14 15 print(next(e)) 16 17 print(e.send('包子')) 18 print(e.send('韭菜餡包子')) 19 print(e.send('大蒜包子')) 20 21 ### 22 鋼蛋 start to eat food 23 [] 24 鋼蛋 get 包子 ,to start eat 25 done 26 ['包子'] 27 鋼蛋 get 韭菜餡包子 ,to start eat 28 done 29 ['包子', '韭菜餡包子'] 30 鋼蛋 get 大蒜包子 ,to start eat 31 done 32 ['包子', '韭菜餡包子', '大蒜包子']yield的表達式形式:
1 yield的表達式形式: 2 food=yield 3 4 def eater(name): 5 print('%s start to eat' %name) 6 while True: 7 food=yield 8 print('%s eat %s' %(name,food)) 9 10 e=eater('鋼蛋') 11 12 #e.send與next(e)的區別 13 #1.如果函數內yield是表達式形式,那么必須先next(e) 14 #2.二者的共同之處是都可以讓函數在上次暫停的位置繼續運行,不一樣的地方在于<br># send在觸發下一次代碼的執行時,會順便給yield傳一個值7,生成器總結
生成器是這樣一個函數,它記住上一次返回時在函數體中的位置。對生成器函數的第二次(或第 n 次)調用跳轉至該函數中間,而上次調用的所有局部變量都保持不變。
生成器不僅“記住”了它數據狀態;生成器還“記住”了它在流控制構造(在命令式編程中,這種構造不只是數據值)中的位置。
生成器的特點:
?
1 from collections import Iterator 2 #生成器就是一個函數,這個函數內包含有yield這個關鍵字 3 def test(): 4 print('one') 5 yield 1 #return 1 6 7 8 g=test() 9 print(g) 10 print(isinstance(g,Iterator)) 11 12 ### 13 <generator object test at 0x000001E80F8F0780> 14 True 15 16 --------------------------------------------------------------------- 17 from collections import Iterator 18 #生成器就是一個函數,這個函數內包含有yield這個關鍵字 19 def test(): 20 print('one') 21 yield 1 #return 1 22 23 24 g=test() 25 print(g) 26 print(isinstance(g,Iterator)) 27 28 print(next(g)) 29 30 ### 31 <generator object test at 0x0000023F1F5E0780> 32 True 33 one #調用next方法時,生成器才執行 34 1 35 -------------------------------------------------------------- 36 def countdown(n): 37 print('start coutdown') 38 while n > 0: 39 yield n #1 40 n-=1 41 print('done') 42 43 g=countdown(5) 44 # print(g) 45 46 # print(next(g)) 47 # print(next(g)) 48 # print(next(g)) 49 # print(next(g)) 50 # print(next(g)) 51 # print(next(g)) 52 53 # for i in g: #iter(g) 54 # print(i) 55 56 # while True: 57 # try: 58 # print(next(g)) 59 # except StopIteration: 60 # break 61 62 ------------------------------------------ 63 >>> def createGenerator() : 64 ... mylist = range(3) 65 ... for i in mylist : 66 ... yield i*i 67 ... 68 >>> mygenerator = createGenerator() # create a generator 69 >>> print(mygenerator) # mygenerator is an object! 70 <generator object createGenerator at 0xb7555c34> 71 >>> for i in mygenerator: 72 ... print(i) 73 0 74 1 75 4 示例8,迭代式
迭代器是一個可以記住遍歷的位置的對象。
迭代器對象從集合的第一個元素開始訪問,直到所有的元素被訪問完結束。迭代器只能往前不會后退。
迭代器有兩個基本的方法:iter()?和?next()。
迭代器就類似一個循環,迭代一次,就是相當于循環一次。
?
可以直接作用于for循環的數據類型:
- 一類是集合數據類型,如list、tuple、dict、set、str等;
- 一類是generator(生成器),包括生成器和帶yield的 generator function。
1,可迭代對象
? 直接作用于for循環的對象統稱為可迭代對象:Iterable。
1 可以使用isinstance()判斷一個對象是否是Iterable對象: 2 >>> from collections import Iterable 3 >>> isinstance([], Iterable) 4 True 5 >>> isinstance({}, Iterable) 6 True 7 >>> isinstance('abc', Iterable) 8 True 9 >>> isinstance((x for x in range(10)), Iterable) 10 True 11 >>> isinstance(100, Iterable) 12 False而生成器不但可以作用于for循環,還可以被next()函數不斷調用并返回下一個值,直到最后拋出Stopiteration錯誤表示無法繼續。
2,迭代器:是一種數據流
可以被next()函數調用并不斷返回下一個值的對象稱為迭代器:Iterator
1 可以使用isinstance()判斷一個對象是否是Iterator對象: 2 3 >>> from collections import Iterator 4 >>> isinstance((x for x in range(10)), Iterator) 5 True 6 >>> isinstance([], Iterator) 7 False 8 >>> isinstance({}, Iterator) 9 False 10 >>> isinstance('abc', Iterator) 11 False生成器都是Iterator對象,但list、dict、str雖然是Iterable,卻不是Iterator。
把list、dict、str等Iterable變成Iterator可以使用iter()函數:
1 >>> isinstance(iter([]), Iterator) 2 True 3 >>> isinstance(iter('abc'), Iterator) 4 True3,小結
凡是可作用于for循環的對象都是Iterable類型;
凡是可作用于next()函數的對象都是Iterator類型,它們表示一個惰性計算的序列;
集合數據類型如list、dict、str等是Iterable但不是Iterator,不過可以通過iter()函數獲得一個Iterator對象。
?
Python3的for循環本質上就是通過不斷調用next()函數實現的,例如:
1 for x in [1, 2, 3, 4, 5]: 2 pass實際上完全等價于:
1 # 首先獲得Iterator對象: 2 it = iter([1, 2, 3, 4, 5]) 3 # 循環: 4 while True: 5 try: 6 # 獲得下一個值: 7 x = next(it) 8 except StopIteration: 9 # 遇到StopIteration就退出循環 10 break9,函數中的一些大坑
①函數的默認函數一定要是不變對象(immutable)
- str
- int
- float
- tuple
- 數值型(number)
看下面一個例子:
1 def foo(bar=[]): 2 bar.append('a') 3 return bar 4 print(foo())#['a'] 5 print(foo())#['a','a'] 6 print(foo())#['a','a','a']乍一眼一看,每次調用foo(),變量bar都應該重置為[]啊,為什么上一次的結果會進行保留呢?
從Python文檔中可以找到這樣一句話
翻譯過來就是:重要警告:默認值只計算一次。當默認值是可變對象(如列表,字典或大多數類的實例)時,這會有所不同。例如,以下函數會累積在后續調用中傳遞給它的參數。
這個原因是由于默認參數只計算一次,因為list 是可變數據類型,函數每次調用時,L 是同一個對象的引用。就相當于全局變量一般了
def foo(bar=None):if bar is None:bar=[]bar.append('a') print(foo())#['a'] print(foo())#['a'] print(foo())#['a']記住,默認參數一定要是不可變類型
?
轉載于:https://www.cnblogs.com/herosyuan/p/10043238.html
總結
以上是生活随笔為你收集整理的python-函数进阶的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CI框架简单使用
- 下一篇: 如何正确选择开源数据库?你需要这的5个技