python迭代器-生成器-列表推導式-生成器表示式-day11-12
生成器Generator
生成器函式
一個包含yield關鍵字的函式就是一個生成器函式。yield可以為我們從函式中返回值,但是yield又不同於return,return的執行意味著程式的結束,呼叫生成器函式不會得到返回的具體的值,而是得到一個可迭代的物件。每一次獲取這個可迭代物件的值,就能推動函式的執行,獲取新的返回值。直到函式執行結束
def func():
print('hello')
yield 1
g = func() #呼叫生成器函式
print(g) #g是一個generator生成器
結果:
<generator object func at 0x000001F22C0D0EB8>
#呼叫就不會執行這個函式,而是返回一個生成器
print(dir(g)) #g是一個迭代器,所以所有的生成器都是迭代器
a = g.__next__()
print(a)
結果:
hello
1
事例二:next本質就是呼叫__next__
def genrator_fun1(): #1 #5
a = 1 #6
print('現在定義了a變數') #7
yield a #8 #10
b = 2 #11
print('現在又定義了b變數') #12
yield b #13
g1 = genrator_fun1() #2
#列印g1可以發現g1就是一個生成器
print('g1 : ',g1) #3
print(next(g1)) #4
print(next(g1)) #9
結果:
g1 : <generator object genrator_fun1 at 0x00000195435B0EB8>
現在定義了a變數
1
現在又定義了b變數
2
yield關鍵字的特點: 可以記錄當前函式中執行的位置,下一次繼續執行
next和yield是一對搭檔 : next開始函式的執行 yield停止函式的執行
生成器函式最大的特點:
呼叫之後不執行,需要next來觸發這個函式繼續向下執行
一個函式中可以有多個yield
def func():
yield 1 #停止符,也是一個記錄符(到這裡num1就執行完了返回結果給num1)
print(123) #接著上次的程式碼開始執行
yield 2 #遇到yield又停止了,返回結果給num2
for i in range(2):
print(i)
yield 3
g = func()
num1 = g.__next__()
print(num1)
num2 = g.__next__() #num1函式執行完後就開始執行num2
print(num2)
num3 = g.__next__()
print(num3)
結果
1
123
2
0
1
3
事例二
def f():
print(111)
yield 1
print(222)
yield 3
print(333)
yield 5
f1 = f()
for i in f1:
print('*'*i)
# num1 = next(f1)
# num2 = next(f1)
# num3 = next(f1)
# print(num1,num2,num3)
結果
111
*
222
***
333
*****
執行效率對比,明顯yield一次取10個數比下邊這個一次生成所有資料,然後再從所有的資料中判斷取值效率要高的多
def func1():
for i in range(10000000):
yield (i+1)%10
g = func1()
for i in range(10):
print(g.__next__())
----------------------------
def func2():
new_l = []
for i in range(10000000):
new_l.append((i+1)%10)
return new_l
ret = func2()
count = 0
for i in ret:
if count < 10:
print(i)
else:
break
count+= 1
iter_ret = ret.__iter__()
for i in range(10):
iter_ret.__next__()
#做2000001件衣服,前兩次每次輸出1件,後邊一次輸出80件
def get_cloth():
for i in range(1,2000001):
yield '第%s件衣服'%i
g = get_cloth()
print(g.__next__()) #這是我已經執行過的第1件衣服
print(g.__next__()) #這是我已經執行過的第2件衣服
for i in range(80):
print(g.__next__()) #下面我開始一次要80件就從3件開始執行82結束
# 監聽檔案的輸入,對於檔案中隨時輸入的內容進行自動化分析/自動化展示
def get_line(f):
f = open('f',encoding='utf-8')
while True:
line = f.readline().strip()
if not line:
continue
yield line
line_g = get_line(file)
for line in line_g:
print(line.split(','))
總結
可迭代物件:
擁有__iter__方法
特點:惰性運算
例如:range(),str,list,tuple,dict,set
迭代器Iterator:
擁有__iter__方法和__next__方法
例如:iter(range()),iter(str),iter(list),iter(tuple),iter(dict),iter(set),reversed(list_o),map(func,list_o),filter(func,list_o),file_o
生成器Generator:
本質:迭代器,所以擁有__iter__方法和__next__方法
特點:惰性運算,開發者自定義
使用生成器的優點:
1.延遲計算,一次返回一個結果。不會一次生成所有的結果,這對於大資料量處理,將會非常有用
2.提高程式碼可讀性
send 獲取下一個值的效果和next基本一致
只是在獲取下一個值的時候,給上一yield的位置傳遞一個數據
send使用的注意事項
第一次使用生成器的時候,是用next獲取下一個值
最後一個yield不能接受外部的值
def f():
print(111)
# smell = yield 1 #ret把000傳遞給yield位置上即smell=000
# print(smell)
yield 1 #ret的yield返回值1
print(222)
smell2 = yield 2
print(smell2) #這個是最後一個yield不接受外部的引數所以沒有返回值
yield 3
g = f()
print(g.__next__())
ret = g.send('000') #傳一個值到生成器裡
print(ret)
ret2 = g.send('ccc')
print(ret2)
結果
111
1
222
2
ccc
3
如果啟用上邊的兩行結果
111
1
000
1
222
2
yield from生成器函式呼叫從中取值?
def func():
l1 = [1,2,3]
s2 = 'abc'
l1.extend(s2)
print(l1)
for i in l1:
yield i
g = func()
for i in g:
print(i)
結果:
[1, 2, 3, 'a', 'b', 'c']
1
2
3
a
b
c
事例二
def func():
l1 = [1,2,3]
s2 = 'abc'
yield from l1 #from迴圈的意思,相當於for
yield from s2
g = func()
for i in g:
print(i)
執行結果
1
2
3
a
b
c
事例三
def func():
l1 = [1,2,3]
s2 = 'abc'
for a in l1:
yield a
for j in s2:
yield j
g = func()
for i in g:
print(i)
事例四
def gen2():
yield from 'AB'
yield from range(3)
print(list(gen2()))
等同
def gen1():
for c in 'AB':
yield c
for i in range(3):
yield i
print(list(gen1()))
float 浮點數
1.2
2.3333344
2.56789*10**2 = 256.789
為什麼要叫浮點數 : 因為小數點是浮動的
浮點數能表示的數學上的數字: 有理數(有限小數 無限迴圈小數)
浮點數的小數點是不準確的: 小數的小數位是轉換成二進位制儲存
如果你建立的變數本身帶小數點,那麼這個變數的資料型別直接就是浮點數
所有的除法(除了//)得到的都是小數
浮點數如果太長請轉換成字串儲存
a = '1.71264864975975073507505'
除法
python2.x 整數除以整數就會取整(向下取整),有一個數浮點數,就按照浮點數計算
python3.x 所有的除(除了//)的結果都是小數
生成器函式中
send/__next__ 生成器函式之外用的
yield/yield from 生成器函式之內用的
next+send == yield
如果函式中的yield要接收引數,那麼應該使用send傳值
如果函式中的yield不需要接收引數,那麼應該使用next即可
生成器和迭代器是一樣的,內部的值都只能取一次
從生成器中取值的方式也和迭代器是一樣的:
for
next/send
資料型別的強制轉換(list)
面試題
例題一
def demo():
for i in range(4):
yield i
g=demo()
g1=(i for i in g)
g2=(i for i in g1)
print(list(g1))
print(list(g2)) # 生成器中的資料只能取一次,去完就沒有了
結果
[0, 1, 2, 3]
[]
例題二
def demo():
for i in range(3):
yield i
g=demo() #g=0,1,2
g1=(i for i in g)
g2=(i for i in g1)
print(g2.__next__()) #0 #誰先取值就給誰
print(g2.__next__()) #1
print(g1.__next__()) #2,因為g2把前兩個數給打印出來了,所以只剩下2,就給了g1
執行結果
0
1
2
列表推導式和生成器表示式
sum函式是Python的內建函式,該函式使用迭代器協議訪問物件,而生成器實現了迭代器協議,所以可以直接這樣計算一系列值的和
生成器表示式:幾乎不佔記憶體
print(sum(x ** 2 for x in range(10000000)))
#而不用多此一舉的先構造一個列表:記憶體佔用大,機器容易卡死
#print(sum([x ** 2 for x in range(10000000]))
列表推導式
應用場景:當已經有了一個列表,從這個列表中的每一個元素都需要做某個操作,並且需要將操作的結果放在一個新的列表中,適合使用列表推導式
新的列表 = [每一個元素要做的操作 for 列表中的每一個元素 in 列表]
迴圈/列表轉換成列表推導式
例題一
l = [1,2,3,4,5,6]
new_l = []
for i in l:
new_l.append(i*i) #[1,4,9,16,25,36]
print(new_l)
#列表推導式
new_l2 = [i*i for i in l]
print(new_l2)
等同
l = [1,2,3,4,5,6]
print([i*i for i in l])
例題二
new_lst = []
for i in range(2,20):
new_lst.append(i//3)
print(new_lst)
列表推導式
lst = [i//3 for i in range(2,20)]
print(lst)
例題三
#請計算0-100內能被3整除的所有數字
new_lst = []
for i in range(0,10):
if i%3 == 0:
new_lst.append(i)
print(new_lst)
列表推導式
print([i for i in range(0,10) if i%3 == 0])
生成器表示式(的結果是生成器):
處理比較簡單的邏輯
並且能夠以更少的程式碼來節省時間空間
def func():
for i in range(101):
if i%3 == 0:
yield i
g1 = func()
print(g1.__next__())
生成器表示式
g = (i for i in range(0,101) if i%3 == 0)
print(g.__next__())
print(g.__next__())
#列表推導式
egg_list=['雞蛋%s' %i for i in range(10)]
print(egg_list)
生成器表示式
egg_list=('雞蛋%s' %i for i in range(10))
print(egg_list)
print(next(egg_list))
# 計算移動平均值的例子(沒懂)
#def cal_avg():
# sum_n = 0
# count = 0
# while True:
# if count:
# num = yield sum_n/count # 7/1 num = 9 16/2 = 8 num = 8 24/4 = 8
# else:
# num = yield 0 # num = 7
# sum_n += num # sum_n = 7 + 9 = 16 +8 = 24 +10
# count += 1 # count = 1 + 1 = 2 + 1 = 3 + 1
## 7,9,8,10
#g = cal_avg()
#g.__next__() # 0
#avg1 = g.send(7)
#print(avg1)
#avg2 = g.send(9)
#print(avg2)
#avg3 = g.send(8)
#avg3 = g.send(10)
#def add(n,i):
# return n+i
#def test():
# for i in range(4):
# yield i
#g=test() #g= 0,1,2,3
#for n in [1,10]: #n=1,10
# g=(add(n,i) for i in g) #g= 0,1,2,3所以i=0,1,2,3
#print(list(g)) #因為add=n+i,所以迴圈第一次就等於0+10,第二次就等於1+10
執行結果
[20, 21, 22, 23]