記錄我的 python 學習歷程-Day12 生成器/推導式/內建函式Ⅰ
一、生成器
初識生成器
生成器的本質就是迭代器,在python社群中,大多數時候都把迭代器和生成器是做同一個概念。
唯一的不同就是:
迭代器都是Python給你提供的已經寫好的工具或者通過資料轉化得來的,(比如檔案控制代碼,iter([1,2,3])。
生成器是需要我們自己用python程式碼構建的工具。最大的區別也就如此了。
生成器的構建方式
在 python 中有三種方式來建立生成器:
- 通過生成器函式
- 通過生成器推導式
- python內建函式或者模組提供(其實1,3兩種本質上差不多,都是通過函式的形式生成,只不過1是自己寫的生成器函式,3是python提供的生成器函式而已)
生成器函式
# 函式: def func(): print(111) return 222 ret = func() print(ret) # 結果: # 111 # 222 # 生成器函式: def func(): print(111) yield 222 ret = func() print(ret) # 結果: # <generator object func at 0x102cd3d00>
將函式中的 return 換成 yield,func 就不是函數了,而是一個生成器函式。
由於函式中存在yield,那麼這個函式就是一個生成器函式.我們在執行這個函式的時候.就不再是函式的執行了.而是獲取這個生成器物件
生成器的本質就是迭代器.迭代器如何取值,生成器就如何取值。所以我們可以直接執行next()來執行以下生成器
def func(): print("111") yield 222 gener = func() # 這個時候函式不會執⾏. ⽽是獲取到⽣成器 ret = next(gener) # 這個時候函式才會執⾏ 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, 10001): lst.append(f'{i}號包子') return lst e = eat() print(e)
這樣做沒有問題,但是我們由於學生沒有那麼多,只吃了2000個左右,剩下的8000個,就只能佔著一定的空間,放在一邊了。如果包子鋪老闆效率夠高,我吃一個包子,你做一個包子,那麼這就不會佔用太多空間儲存了,完美。
def gen_eat(): for i in range(1, 10001): yield f'{i}號包子' e = gen_eat() for i in range(200): print(next(e)) for i in range(300): print(next(e)) # 多次next包子的號碼是按照順序記錄的。
這兩者的區別:
第一種:直接把包子全做出來,佔用記憶體。
第二種:吃一個,包一個,非常節省記憶體,而且還可以保留上次的位置。
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) # 衛龍 # 老冰棍 # 北冰洋 # 牛羊配 # 饅頭 # 花捲 # 豆包 # 大餅
二、推導式
列表推導式
列表推導式分為兩種模式:
- 迴圈模式:[變數(加工的變數)for 變數 in iterable(可迭代物件)]
- 篩選模式:[變數(加工的變數)for 變數 in iterable(可迭代物件) if 條件]
迴圈模式
將10以內所有整數的平方寫入列表。
ls = [i**2 for i in range(1, 11)] print(ls)
100以內所有的偶數寫入列表。
ls = [i for i in range(2, 101, 2)] print(ls)
從python1期到python100期寫入列表lst。
ls = [f'python{i}期' for i in range(1, 101)] print(ls)
上面那個格式化輸出的變數f'python{i}期',就是加工的變數。
上面做的那三個就是迴圈模式,比較簡單。
篩選模式
將這個列表中大於3的元素留下來。
l1 = [4, 3, 2, 6, 5, 5, 7, 8] ls = [i for i in l1 if i > 3] print(ls) # [4, 6, 5, 5, 7, 8]
做幾道題:
三十以內可以被三整除的數。
ls = [i for i in range(1, 31) if i % 3 == 0] print(ls) # [3, 6, 9, 12, 15, 18, 21, 24, 27, 30]
過濾掉長度小於3的字串列表,並將剩下的轉換成大寫字母。
l1 = ['Dylan', 'xiaobai', 'ab', '33434', '1b'] ls = [i.upper() for i in l1 if len(i) > 3] print(ls) # ['DYLAN', 'XIAOBAI', '33434']
找到巢狀列表中名字含有兩個‘e’的所有名字(有難度)
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'], ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']] ls = [name.upper() for i in names for name in i if name.count('e') == 2] print(ls) # ['JEFFERSON', 'WESLEY', 'STEVEN', 'JENNIFER']
生成器表示式
生成器表示式和列表推導式的語法上一模一樣,只是把[]換成()就行了。比如將十以內所有數的平方放到一個生成器表示式中。
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迴圈超過三層)這樣就不建議大家用推導式構建。
生成器的惰性機制: 生成器只有在訪問的時候才取值,說白了.你找他要才給你值.不找他要.他是不會執行的.
其它相關推導式(瞭解)
字典推導式
lst1 = ['jay','jj','meet'] lst2 = ['周杰倫','林俊杰','郭寶元'] dic = {lst1[i]:lst2[i] for i in range(len(lst1))} print(dic)
集合推導式
集合推導式可以幫我們直接生成一個集合,集合的特點;無序,不重複 所以集合推導式自帶去重功能
s = {abs(i) for i in lst} print(s)
三、內建函式 I
本節我們講內建函式。 首先來說,函式就是以功能為導向,一個函式封裝一個功能,那麼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]))