函式進階三(生成器、生成器表示式、匿名函式)
阿新 • • 發佈:2019-01-03
一. 昨日內容回顧
函式名的運用:
1.特殊的變數
2.函式名可以當作變數賦值
3.函式名可以當作容器類型別的元素
4.函式名可以當作函式的引數
5.函式名可以當作函式的返回值
閉包:
1.內層函式對外層函式(非全域性)變數的引用和改變
2.閉包只存在於內層函式中
3.閉包都要逐層返回,最終返回給最外層函式
閉包的特點:
直譯器遇到閉包,不會隨著函式的結束而結束空間
閉包應用:
裝飾器
爬蟲
可迭代物件
判斷方法:
obj
"__iter__" in dir(obj)
from collections import Iterable
isinstance(obj, Iterable)
可迭代物件不能直接取值,必須轉化成迭代器取值(__next__)
# 迭代器
"__iter__" in dir(obj)
from collections import Iterable
isinstance(obj, Iterator)
while迴圈模擬for迴圈
二. 生成器
生成器的本質就是迭代器,生成器是自己用Python程式碼寫的迭代器
生成器函式
生成器表示式
s1 = "ajlsdj"
iter(s1) # 這不是手寫,是通過Python寫的
將一個函式變成生成器函式
只要有 yield 就不是函式,可看成生成器
# 生成器函式 def func(): print(111) print(222) yield 666 # yield "aklsj" # print(func()) # <generator object func at 0x000001AE6CB50BF8> 返回的是生成器物件 ret = func()print(next(ret)) # 這裡一個 next 對應一個 yield # print(next(ret)) # 所以這裡會報錯 StopIteration,除非上面再加個 yield # 111 # 222 # 666
# yield 和 return 的共同點和區別 # 1. 區別:return 終止函式,而 yield 不會終止生成器函式 # 2. 共同點:都會返回一個值,return給函式的執行者返回值,yield是給next()返回值 # 示例: def cloth(): for i in range(1, 5001):print("我寫的程式碼沒有bug 衣服%s號" % i) cloth() # 我寫的程式碼沒有bug 衣服1號 # 我寫的程式碼沒有bug 衣服2號 # 。。。 # 我寫的程式碼沒有bug 衣服5000號 def cloth1(): for i in range(1, 5001): yield "有志青年 衣服%s號" % i genor = cloth1() for i in range(1, 101): print(next(genor)) for i in range(1, 51): print(next(genor)) # 有志青年 衣服1號 # 有志青年 衣服2號 # 。。。 # 有志青年 衣服100號 # 有志青年 衣服101號 # 。。。 # 有志青年 衣服149號 # 有志青年 衣服150號 # 由列印結果可知,生成器會記錄上一次執行的位置,再次執行時從上一次位置開始計數
# 以下很重要,必須掌握 # send next def func(): yield 666 yield "abc" yield "速度" yield "hxy" genor = func() print(next(genor)) # 666 print(next(genor)) # abc print(genor.send(None)) # send() 必須要有一個引數 # 666 print(genor.send(None)) # abc # send 不僅能對應 yield 取值,而且可以給上一個 yield 傳送一個值 def func(): count = yield 666 print(count) s = yield "abc" print(s) yield "速度" yield "hxy" genor = func() print(genor.send(None)) # 666 print(genor.send("alex")) # alex abc print(genor.send("111")) # 111 速度 # 第一個取值能否用 send 傳參? # 不能,比如上面的 print(genor.send(None)) 中的 None 改為 其他的,那麼會報錯 # 因為 send 是給上一個 yield 傳送一個值,第一個上面沒有值了 # 最後一個 yield 永遠也得不到 send 傳的值。
# yield from 將一個可迭代物件變成一個迭代器返回 def func(): lst = ["abc", "def", "ghi", "jkl"] yield lst genor = func() print(next(genor)) # ['abc', 'def', 'ghi', 'jkl'] for i in genor: print(i) # ['abc', 'def', 'ghi', 'jkl'] def func(): lst = ["abc", "def", "ghi", "jkl"] yield from lst # 注意這裡與上面的區別 genor = func() print(next(genor)) # abc print(next(genor)) # def print(next(genor)) # ghi print(next(genor)) # jkl for i in genor: print(i) # abc # def # ghi # jkl
三. 列表推導式,生成器表示式
列表推導式——用一行程式碼構建一個簡單或較複雜的列表
好處:減少程式碼量
li = [i for i in range(1, 101)]
print(li)
# 列表推導式分三種情況: # 1. 迴圈模式——[變數(加工後的變數) for 變數 in iterable] # 構建一個列表:['python1期', 'python2期', ...'python25期'] print(["python%i期" % i for i in range(1, 26)]) # 2. 篩選模式——[變數(加工後的變數) for 變數 in iterable if 條件] # 30以內所有的偶數(兩種方法) print([i for i in range(2, 31, 2)]) print([i for i in range(1, 31) if i % 2 == 0]) # 其他示例 print([i for i in range(1, 31) if i % 3 == 0]) print([i**2 for i in range(1, 31) if i % 3 == 0]) print(["地球%s號" % i for i in range(1, 100, 2)]) # 3. 三元模式——迴圈模式的變異 # 構建一個列表,裡面的元素是1-20,能被3整除的元素要替換成* # print("*" if 3 > 2 else 1) print(["*" if i % 3 == 0 else i for i in range(1, 21)]) # 將至少含有兩個e的名字放到一個列表 names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']] # 方法一 l = [] for i in names: for j in i: if j.count("e") >= 2: l.append(j) print(l) # 方法二 print([j for i in names for j in i if j.count("e") >= 2]) # 列表推導式總結: # 優點:一行搞定,節省程式碼行數 # 缺點:不能用debug模式,列表推導式相對複雜的列表不能用,三層以上效率低也很難寫
# 生成器表示式 # 它與列表推導式寫法幾乎一樣,也有同樣的三種模式,只需將[]換成()就可以 # 列表推導式 print(["python%i期" % i for i in range(1, 26)]) # 生成器表示式 ret = ("python%i期" % i for i in range(1, 26)) for i in ret: print(i) # 元組沒有推導式!!!因為上面的生成器表示式已經用()來表示了 # 字典推導式 print({i:None for i in range(1, 10)}) # {1: None, 2: None, 3: None, 4: None, 5: None, 6: None, 7: None, 8: None, 9: None} # 將下面字典的鍵值對調 mcase = {"a": 10, "b": 34, "c":20, "d":15} print({v:k for k,v in mcase.items()}) # 集合推導式 # 將{1, -2, 3, -4, 4}的所有元素取平方 set1 = {1, -2, 3, -4, 4} print({i**2 for i in set1}) # {16, 1, 4, 9} 順便去重了
# 四. 匿名函式lambda def func(x, y): return x + y print(func(3, 4)) # 像上面這種只有return 即返回值的函式,可以使用匿名函式 lambda # 匿名函式只能用一行程式碼表示,冒號前面是引數,後面是返回值 func2 = lambda x,y: x + y # func2就是這個匿名函式的名字 print(func2(3, 4)) # 寫一個匿名函式,需要三個數字引數,返回值為三個數想乘的結果 func3 = lambda a, b, c: a * b * c print(func3(1, 2, 3))