python基礎(14):生成器、列表推導式

1. 生成器

什麼是⽣成器?⽣成器實質就是迭代器。
在python中有三種⽅式來獲取⽣成器:
1. 經過⽣成器函數
2. 經過各類推導式來實現⽣成器
3. 經過數據的轉換也能夠獲取⽣成器
⾸先,咱們先看⼀個很簡單的函數:
def func():
  print("111")
  return 222
ret = func()
print(ret)

結果:
111 222
將函數中的return換成yield就是⽣成器
def func():
  print("111")
  yield 222
ret = func()
print(ret)

結果:
<generator object func at 0x10567ff68>
運⾏的結果和上⾯不⼀樣,爲何呢?因爲函數中存在了yield,那麼這個函數就是⼀個⽣成器
函數。這個時候,咱們再執⾏這個函數的時候,就再也不是函數的執⾏了,⽽是獲取這個⽣成器。
如何使⽤呢? 想一想迭代器,⽣成器的本質是迭代器,因此,咱們能夠直接執⾏__next__()來執⾏如下⽣成器。
def func():
  print("111")
  yield 222
gener = func() # 這個時候函數不會執⾏. ⽽是獲取到⽣成器
ret = gener.__next__() # 這個時候函數纔會執⾏. yield的做⽤和return⼀樣. 也是返回數據
print(ret)

結果:
111 222
那麼咱們能夠看到,yield和return的效果是⼀樣的,有什麼區別呢? yield是分段來執⾏⼀個
函數,return呢? 直接停⽌執⾏函數。
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__()程序報錯, 也就是說. 和return⽆關了.
print(ret3)

結果:
111 Traceback (most recent call last): 222 333   File "/Users/sylar/PycharmProjects/oldboy/iterator.py", line 55, in <module> 444   ret3 = gener.__next__() # 最後⼀個yield執⾏完畢. 再次__next__()程序報錯, 也就是說. 和return⽆關了. StopIteration
當程序運⾏完最後⼀個yield,那麼後⾯繼續進⾏__next__()程序會報錯。好了⽣成器說完了,⽣成器有什麼做⽤呢? 咱們來看這樣⼀個需求,老男孩向JACK JONES訂
購10000套學⽣服,JACK JONES就比較實在,直接造出來10000套衣服。
def cloth():
  lst = []
  for i in range(0, 10000):
    lst.append("⾐服"+str(i))
  return lst
cl = cloth()
可是呢,問題來了。老男孩如今沒有這麼多學⽣啊,⼀次性給我這麼多,我往哪⾥放啊. 很尷尬啊,最好的效果是什麼樣呢? 我要1套,你給我1套,⼀共10000套,是否是最完美的。
 
def cloth():
  for i in range(0, 10000):
    yield "⾐服"+str(i)
cl = cloth()
print(cl.__next__())
print(cl.__next__())
print(cl.__next__())
print(cl.__next__())
區別: 第⼀種是直接⼀次性所有拿出來,會很佔⽤內存,第⼆種使⽤⽣成器,⼀次就⼀個. ⽤多少⽣成多少。⽣成器是⼀個⼀個的指向下⼀個,不會回去,__next__()到哪,指針就指到哪⼉,下⼀次繼續獲取指針指向的值。
接下來咱們來看send⽅法,send和__next__()⼀樣均可以讓⽣成器執⾏到下⼀個yield。
def eat():
  print("我吃什麼啊")
  a = yield "饅頭"
  print("a=",a)
  b = yield "⼤餅"
  print("b=",b)
  c = yield "⾲菜盒⼦"
  print("c=",c)
  yield "GAME OVER"

gen
= eat() # 獲取⽣成器 ret1 = gen.__next__() print(ret1) ret2 = gen.send("胡辣湯") print(ret2) ret3 = gen.send("狗糧") print(ret3) ret4 = gen.send("貓糧") print(ret4)
send和__next__()區別:
1. send和next()都是讓⽣成器向下走⼀次。
2. send能夠給上⼀個yield的位置傳遞值,不能給最後⼀個yield發送值,在第⼀次執⾏⽣成器代碼的時候不能使⽤send()。
⽣成器能夠使⽤for循環來循環獲取內部的元素:
def func():
  print(111)
  yield 222
  print(333)
  yield 444
  print(555)
  yield 666

gen
= func() for i in gen:   print(i)   
結果:
111 222 333 444 555 666

2. 列表推導式, ⽣成器表達式以及其餘推導式

⾸先咱們先看⼀下這樣的代碼,給出⼀個列表,經過循環,向列表中添加1-13 :
lst = []
for i in range(1, 15):
  lst.append(i)
print(lst)
替換成列表推導式:
lst = [i for i in range(1, 15)]
print(lst)
列表推導式是經過⼀⾏來構建你要的列表,列表推導式看起來代碼簡單,可是出現錯誤以後很難排查。
列表推導式的常⽤寫法:
[ 結果 for 變量 in 可迭代對象]
例,從python1期到python14期寫入列表lst:
lst = ['python%s' % i for i in range(1,15)]
print(lst)
咱們還能夠對列表中的數據進⾏篩選
篩選模式:
[ 結果 for 變量 in 可迭代對象 if 條件 ]
# 獲取1-100內全部的偶數
lst = [i for i in range(1, 100) if i % 2 == 0]
print(lst)
⽣成器表達式和列表推導式的語法基本上是⼀樣的,只是把[]替換成()
gen = (i for i in range(10))
print(gen)

結果:
<generator object <genexpr> at 0x106768f10>
打印的結果就是⼀個⽣成器,咱們能夠使⽤for循環來循環這個⽣成器:
gen = ("麻花藤我第%s次愛你" % i for i in range(10))
for i in gen:
  print(i)
⽣成器表達式也能夠進⾏篩選: # 獲取1-100內能被3整除的數
gen = (i for i in range(1,100) if i % 3 == 0)
for num in gen:
  print(num)

# 100之內能被3整除的數的平⽅ gen = (i * i for i in range(100) if i % 3 == 0) for num in gen:   print(num)
# 尋找名字中帶有兩個e的⼈的名字 names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven','Joe'], ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
# 不⽤推導式和表達式 result = [] for first in names:   for name in first:     if name.count("e") >= 2:       result.append(name) print(result)
# 推導式 gen = (name for first in names for name in first if name.count("e") >= 2) for name in gen:   print(name)
⽣成器表達式和列表推導式的區別:
1. 列表推導式比較耗內存,⼀次性加載,⽣成器表達式⼏乎不佔⽤內存,使⽤的時候才分配和使⽤內存。
2. 獲得的值不⼀樣,列表推導式獲得的是⼀個列表,⽣成器表達式獲取的是⼀個⽣成器。
舉個慄⼦。
一樣⼀籃⼦雞蛋,列表推導式: 直接拿到⼀籃⼦雞蛋。⽣成器表達式: 拿到⼀個老⺟雞,須要雞蛋就給你下雞蛋。
⽣成器的惰性機制: ⽣成器只有在訪問的時候才取值,說⽩了,你找他要他纔給你值,不找他要,他是不會執⾏的。
def func():
  print(111)
  yield 222

g
= func() # ⽣成器g g1 = (i for i in g) # ⽣成器g1. 可是g1的數據來源於g g2 = (i for i in g1) # ⽣成器g2. 來源g1
print(list(g)) # 獲取g中的數據. 這時func()纔會被執⾏. 打印111.獲取到222. g完畢. print(list(g1)) # 獲取g1中的數據. g1的數據來源是g. 可是g已經取完了. g1 也就沒有數據print(list(g2)) # 和g1同理
深坑==> ⽣成器,要值得時候纔拿值。
字典推導式:
根據名字應該也能猜到,推到出來的是字典。
# 把字典中的key和value互換
dic = {'a': 1, 'b': '2'}
new_dic = {dic[key]: key for key in dic}
print(new_dic)

# 在如下list中. 從lst1中獲取的數據和lst2中相對應的位置的數據組成⼀個新字典 lst1 = ['jay', 'jj', 'sylar'] lst2 = ['周杰倫', '林俊杰', '邱彥濤'] dic = {lst1[i]: lst2[i] for i in range(len(lst1))} print(dic)
集合推導式:
集合推導式能夠幫咱們直接⽣成⼀個集合。集合的特色: ⽆序,不重複,因此集合推導式⾃帶去重功能。
lst = [1, -1, 8, -8, 12]
# 絕對值去重
s = {abs(i) for i in lst}
print(s)
總結: 推導式有,列表推導式,字典推導式,集合推導式,沒有元組推導式。
⽣成器表達式:
(結果 for 變量 in 可迭代對象 if 條件篩選)
⽣成器表達式能夠直接獲取到⽣成器對象,⽣成器對象能夠直接進⾏for循環,⽣成器具備
惰性機制。
相關文章
相關標籤/搜索