1 生成器
1.1 初識生成器
什么是生成器?這個概念比較模糊,各種文獻都有不同的理解,但是核心基本相同。生成器的本質就是迭代器,在python社區中,大多數時候都把迭代器和生成器是做同一個概念。不是相同么?為什么還要創建生成器?生成器和迭代器也有不同,唯一的不同就是:迭代器都是Python給你提供的已經寫好的工具或者通過數據轉化得來的,(比如文件句柄,iter([1,2,3])。生成器是需要我們自己用python代碼構建的工具。最大的區別也就如此了。
1.2 生成器的構建方式
在python中有三種方式來創建生成器:
1. 通過生成器函數
2. 通過生成器推導式
3. python內置函數或者模塊提供(其實1,3兩種本質上差不多,都是通過函數的形式生成,只不過1是自己寫的生成器函數,3是python提供的生成器函數而已)
1.3 生成器函數
我們先來研究通過生成器函數構建生成器。
首先,我們先看一個很簡單的函數:
def func(): print(11) return 22 ret = func() print(ret) # 運行結果: 11 22
將函數中的return換成yield,這樣func就不是函數了,而是一個生成器函數
def func(): print(11) yield 22
我們這樣寫沒有任何的變化,這是為什么呢? 我們來看看函數名加括號獲取到的是什么?
def func(): print(11) yield 22 ret = func() print(ret) # 運行結果: <generator object func at 0x000001A575163888>
運行的結果和最上面的不一樣,為什么呢?? 由於函數中存在yield,那么這個函數就是一個生成器函數.
我們在執行這個函數的時候.就不再是函數的執行了.而是獲取這個生成器對象,那么生成器對象如何取值呢?
之前我們說了,生成器的本質就是迭代器.迭代器如何取值,生成器就如何取值。所以我們可以直接執行next()來執行以下生成器
def func(): print("111") yield 222 gener = func() # 這個時候函數不會執⾏. ⽽是獲取到⽣成器 ret = gener.__next__() # 這個時候函數才會執⾏ print(ret) # 並且yield會將func生產出來的數據 222 給了 ret。 結果: 111 222
並且我的生成器函數中可以寫多個yield。
def func(): print("111") yield 222 print("333") yield 444 gener = func() ret = gener.__next__() print(ret) ret2 = gener.__next__() print(ret2) ret3 = gener.__next__() # 最后⼀個yield執⾏完畢. 再次__next__()程序報錯 print(ret3) 結果: 111 222 333 444
當程序運行完最后一個yield,那么后面繼續運行next()程序會報錯,一個yield對應一個next,next超過yield數量,就會報錯,與迭代器一樣。
yield與return的區別:
return一般在函數中只設置一個,他的作用是終止函數,並且給函數的執行者返回值。
yield在生成器函數中可設置多個,他並不會終止函數,next會獲取對應yield生成的元素。
舉例:
我們來看一下這個需求:老男孩向樓下賣包子的老板訂購了10000個包子.包子鋪老板非常實在,一下就全部都做出來了
def eat(): lst = [] for i in range(1,10000): lst.append('包子'+str(i)) return lst e = eat() print(e)
這樣做沒有問題,但是我們由於學生沒有那么多,只吃了2000個左右,剩下的8000個,就只能占着一定的空間,放在一邊了。如果包子鋪老板效率夠高,我吃一個包子,你做一個包子,那么這就不會占用太多空間存儲了,完美。
def eat(): for i in range(1,10000): yield '包子'+str(i) e = eat() for i in range(200): next(e)
這兩者的區別:
第一種是直接把包子全部做出來,占用內存。
第二種是吃一個生產一個,非常的節省內存,而且還可以保留上次的位置。
def eat(): for i in range(1,10000): yield '包子'+str(i) e = eat() for i in range(200): next(e) for i in range(300): next(e) # 多次next包子的號碼是按照順序記錄的。
1.4 send 方法(了解,不講)
·接下來我們再來認識一個新的東西,send方法

# next只能獲取yield生成的值,但是不能傳遞值。 def gen(name): print(f'{name} ready to eat') while 1: food = yield print(f'{name} start to eat {food}') dog = gen('alex') next(dog) next(dog) next(dog) # 而使用send這個方法是可以的。 def gen(name): print(f'{name} ready to eat') while 1: food = yield 222 print(f'{name} start to eat {food}') dog = gen('alex') next(dog) # 第一次必須用next讓指針停留在第一個yield后面 # 與next一樣,可以獲取到yield的值 ret = dog.send('骨頭') print(ret) def gen(name): print(f'{name} ready to eat') while 1: food = yield print(f'{name} start to eat {food}') dog = gen('alex') next(dog) # 還可以給上一個yield發送值 dog.send('骨頭') dog.send('狗糧') dog.send('香腸')
send和next()區別:
相同點:
send 和 next()都可以讓生成器對應的yield向下執行一次。
都可以獲取到yield生成的值。
不同點:
第一次獲取yield值只能用next不能用send(可以用send(None))。
send可以給上一個yield置傳遞值。
1.4 yield from
在python3中提供一種可以直接把可迭代對象中的每一個數據作為生成器的結果進行返回

# 對比yield 與 yield from def func(): lst = ['衛龍','老冰棍','北冰洋','牛羊配'] yield lst g = func() print(g) print(next(g)) # 只是返回一個列表 def func(): lst = ['衛龍','老冰棍','北冰洋','牛羊配'] yield from lst g = func() print(g) # 他會將這個可迭代對象(列表)的每個元素當成迭代器的每個結果進行返回。 print(next(g)) print(next(g)) print(next(g)) print(next(g)) ''' yield from ['衛龍','老冰棍','北冰洋','牛羊配'] 等同於: yield '衛龍' yield '老冰棍' yield '北冰洋' yield '牛羊配' '''
有個小坑,yield from 是將列表中的每一個元素返回,所以 如果寫兩個yield from 並不會產生交替的效果

def func(): lst1 = ['衛龍','老冰棍','北冰洋','牛羊配'] lst2 = ['饅頭','花卷','豆包','大餅'] yield from lst1 yield from lst2 g = func() for i in g: print(i)
2. 推導式
本節我們講列表推導式,生成器表達式以及其他推導式,我認為推導式就是構建比較有規律的列表,生成器,字典等一種簡便的方式。那么他如何簡便呢?看下面的例題:
2.1列表推導式
這里讓學生自己做一下,首先我們先看一下這樣的代碼,給出一個列表,通過循環,想列表中添加1~10:

li = [] for i in range(10): li.append(i) print(li)
那么按照上面的要求我們用列表推導式寫一下:

ls = [i for i in range(10)] print(ls)
怎么樣?一行搞定,上面這個代碼就是列表推導式,接下來我們將列表推導式進行一個分類:
列表推導式分為兩種模式:
1.循環模式:[變量(加工的變量) for 變量 in iterable]
2.篩選模式: [變量(加工的變量) for 變量 in iterable if 條件]
當然還有多層循環的,這個我們一會就會講到,那么我們先來看循環模式。
2.2 循環模式
剛才我們看到的就是循環模式,那么有同學會問到,什么叫' 加工的變量'? 這個也比較簡單,接下來我們做幾道題:
-
將10以內所有整數的平方寫入列表。

l1 = [i*i for i in range(1,11)] print(l1)
-
100以內所有的偶數寫入列表.

l1 = [i for i in range(2,101,2)] print(l1)
-
從python1期到python100期寫入列表lst

lst = [f'python{i}' % i for i in range(1,19)] print(lst)
上面那個格式化輸出的變量f'python{i}',就是加工的變量。
上面做的那三個就是循環模式,比較簡單,接下來我們研究篩選模式。
2.3 篩選模式
篩選模式就是在上面的基礎上加上一個判斷條件,將滿足條件的變量留到列表中。
帶着同學們做一個題:
將這個列表中大於3的元素留下來。

l1 = [4, 3, 2, 6, 5, 5, 7, 8] print([i for i in l1 if i > 3])
通過我給大家的演示,大家做幾道題:
-
三十以內可以被三整除的數。
multiples = [i for i in range(30) if i % 3 is 0] print(multiples)
-
過濾掉長度小於3的字符串列表,並將剩下的轉換成大寫字母
l = ['wusir', 'laonanhai', 'aa', 'b', 'taibai'] # print([i.upper() for i in l if len(i) > 3])
-
找到嵌套列表中名字含有兩個‘e’的所有名字(有難度)
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'], ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']] print([name for lst in names for name in lst if name.count('e') >= 2]) # 注意遍歷順序,這是實現的關鍵
列表推導式基本上講完了,當然今天會做一些有關列表推導式的題,讓大家更加深入的了解。
2.4 生成器表達式
生成器表達式和列表推導式的語法上一模一樣,只是把[]換成()就行了。比如將十以內所有數的平方放到一個生成器表達式中

gen = (i**2 for i in range(10)) print(gen) # 結果: <generator object <genexpr> at 0x0000026046CAEBF8>
生成器表達式也可以進行篩選

# 獲取1-100內能被3整除的數 gen = (i for i in range(1,100) if i % 3 == 0) for num in gen: print(num)
生成器表達式和列表推導式的區別:
-
列表推導式比較耗內存,所有數據一次性加載到內存。而.生成器表達式遵循迭代器協議,逐個產生元素。
-
得到的值不一樣,列表推導式得到的是一個列表.生成器表達式獲取的是一個生成器
-
列表推導式一目了然,生成器表達式只是一個內存地址。
無論是生成器表達式,還是列表推導式,他只是Python給你提供了一個相對簡單的構造方式,因為使用推導式非常簡單,所以大多數都會為之着迷,這個一定要深重,推導式只能構建相對復雜的並且有規律的對象,對於沒有什么規律,而且嵌套層數比較多(for循環超過三層)這樣就不建議大家用推導式構建。
生成器的惰性機制: 生成器只有在訪問的時候才取值,說白了.你找他要才給你值.不找他要.他是不會執行的.
2.5 其他相關的推導式(了解):
字典推導式
根據名字應該也能猜到,推到出來的是字典

lst1 = ['jay','jj','meet'] lst2 = ['周傑倫','林俊傑','郭寶元'] dic = {lst1[i]:lst2[i] for i in range(len(lst1))} print(dic)
集合推導式
集合推導式可以幫我們直接生成一個集合,集合的特點;無序,不重復 所以集合推導式自帶去重功能

lst = [1,2,3,-1,-3,-7,9] s = {abs(i) for i in lst} print(s)
3. 內置函數Ⅰ
本節我們講內置函數。 首先來說,函數就是以功能為導向,一個函數封裝一個功能,那么Python將一些常用的功能(比如len)給我們封裝成了一個一個的函數,供我們使用,他們不僅效率高(底層都是用C語言寫的),而且是拿來即用,避免重復早輪子,那么這些函數就稱為內置函數,到目前為止python給我們提供的內置函數一共是68個,由於時間關系以及考慮這些函數的不同重要性我們會挑常用的重要的內置函數去講,就是下面紅色黃色背景的內置函數,剩下的內置函數你們參照着我的博客自己課下練習一下即可。
由於我們這沒有表格的功能,我把這些內置函數進行分類:
黃色一帶而過:all() any() bytes() callable() chr() complex() divmod() eval() exec() format() frozenset() globals() hash() help() id() input() int() iter() locals() next() oct() ord() pow() repr() round()
紅色重點講解:abs() enumerate() filter() map() max() min() open() range() print() len() list() dict() str() float() reversed() set() sorted() sum() tuple() type() zip() dir()
藍色未來會講: classmethod() delattr() getattr() hasattr() issubclass() isinstance() object() property() setattr() staticmethod() super()
上面的黃色,紅色的內置函數是在這兩天講完的(講過的就不講了),藍色的講完面向對象會給大家補充,剩余還有一些課上就不講了,課下練習一下就可以。
eval:執行字符串類型的代碼,並返回最終結果。
eval('2 + 2') # 4 n=81 eval("n + 4") # 85 eval('print(666)') # 666
exec:執行字符串類型的代碼。
s = ''' for i in [1,2,3]: print(i) ''' exec(s)
hash:獲取一個對象(可哈希對象:int,str,Bool,tuple)的哈希值。
print(hash(12322)) print(hash('123')) print(hash('arg')) print(hash('alex')) print(hash(True)) print(hash(False)) print(hash((1,2,3))) ''' -2996001552409009098 -4637515981888139739 1 2528502973977326415 '''
help:函數用於查看函數或模塊用途的詳細說明。
print(help(list)) print(help(str.split))
callable:函數用於檢查一個對象是否是可調用的。如果返回True,object仍然可能調用失敗;但如果返回False,調用對象ojbect絕對不會成功。
name = 'alex' def func(): pass print(callable(name)) # False print(callable(func)) # True
int:函數用於將一個字符串或數字轉換為整型。
print(int()) # 0 print(int('12')) # 12 print(int(3.6)) # 3 print(int('0100',base=2)) # 將2進制的 0100 轉化成十進制。結果為 4
float:函數用於將整數和字符串轉換成浮點數。
complex:函數用於創建一個值為 real + imag * j 的復數或者轉化一個字符串或數為復數。如果第一個參數為字符串,則不需要指定第二個參數。。
print(float(3)) # 3.0 print(complex(1,2)) # (1+2j)
bin:將十進制轉換成二進制並返回。
oct:將十進制轉化成八進制字符串並返回。
hex:將十進制轉化成十六進制字符串並返回。
print(bin(10),type(bin(10))) # 0b1010 <class 'str'> print(oct(10),type(oct(10))) # 0o12 <class 'str'> print(hex(10),type(hex(10))) # 0xa <class 'str'>
divmod:計算除數與被除數的結果,返回一個包含商和余數的元組(a // b, a % b)。
round:保留浮點數的小數位數,默認保留整數。
pow:求x**y次冪。(三個參數為x**y的結果對z取余)
print(divmod(7,2)) # (3, 1) print(round(7/3,2)) # 2.33 print(round(7/3)) # 2 print(round(3.32567,3)) # 3.326 print(pow(2,3)) # 兩個參數為2**3次冪 print(pow(2,3,3)) # 三個參數為2**3次冪,對3取余。
bytes:用於不同編碼之間的轉化。
# s = '你好' # bs = s.encode('utf-8') # print(bs) # s1 = bs.decode('utf-8') # print(s1) # bs = bytes(s,encoding='utf-8') # print(bs) # b = '你好'.encode('gbk') # b1 = b.decode('gbk') # print(b1.encode('utf-8'))
ord:輸入字符找該字符編碼的位置
chr:輸入位置數字找出其對應的字符
# ord 輸入字符找該字符編碼的位置 # print(ord('a')) # print(ord('中')) # chr 輸入位置數字找出其對應的字符 # print(chr(97)) # print(chr(20013))
repr:返回一個對象的string形式(原形畢露)。
# %r 原封不動的寫出來 # name = 'taibai' # print('我叫%r'%name) # repr 原形畢露 print(repr('{"name":"alex"}')) print('{"name":"alex"}')
all:可迭代對象中,全都是True才是True
any:可迭代對象中,有一個True 就是True
# all 可迭代對象中,全都是True才是True # any 可迭代對象中,有一個True 就是True # print(all([1,2,True,0])) # print(any([1,'',0]))