初識生成器與生成器表示式 Day12

陳小木發表於2018-09-18

一、生成器

1,生成器基本概念

生成器的實質是迭代器

迭代器:Iterator  內部同時包含了__iter__()和__next__()函式
可迭代物件:Iterable  內部包含__iter__()函式
迭代器的特點:(同時也是生成器特點)
  1,節省記憶體
  2,惰性機制
  3,不能反覆只能向下執行

在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 0x10647a8e0>

可以看出結果是不一樣的。由於函式中存在了yield,那麼這個函式就是一個生成器函式。我們再執行這個函式的時候,就不再是函式的執行了,而是獲取這個生成器。

因為生成器的本質是迭代器,所以我們可以直接執行__next__()以下生成器:

def func():
    print("111")
    yield 222

gener = func()  # 這個時候函式不會執行,只是獲取到生成器
ret = gener.__next__()  # 函式執行,yield和return的作用一樣,返回資料
print(ret)

結果:
111
222

所以,yield和return的效果是一樣的,都是返回資料,但還是有很大區別的:

yield是分段來執行一個函式;return是直接停止執行函式

 

2,生成器的send()方法:

send()和__next__()一樣都可以讓生成器執行到下一個yield

def eat():
    print("meat")
    a = yield "milk"
    print(`baozi`, a)
    b = yield `zhou`
    print(`guobaorou`, b)
    yield `tea`
    
gen = eat()
ret = gen.__next__()
print(ret)

ret = gen.__next__()
print(ret)

ret = gen.send("MIANBAO")  # 給上一個yield傳值
print(ret)

ret = gen.send("DANGAO")  # 給上一個yield傳值
print(ret)


結果:
meat
milk
baozi None
zhou
guobaorou MIANBAO
tea

Traceback (most recent call last):
    ret = gen.send("DANGAO")  # 給上一個yield傳值
StopIteration

send()和__next__():

  1. send和next都是讓生成器向下走一次

  2. send可以給上一個yield的位置傳遞值,不能給最後一個yield傳送值。也不能在第一次執行生成器程式碼的時候不能使用send()

 

3,使用for迴圈

def func():
    yield "a"
    yield "b"
    yield "c"

gen = func()

for el in gen:
    print(el)

# 結果:
# a
# b
# c

使用for迴圈的話會獲取內部所有的元素

 

二、列表推導式,生成器表示式以及其他推導式

1,列表推導式

常用寫法:[結果 for迴圈 if篩選]

篩選模式:[結果 for 變數 in 可迭代物件 if 條件]

練習題:

# (1)建立列表:[1,3,5,7,9,...99]
l2 = [i for i in range(1,100) if i % 2 == 1]  # [結果 for迴圈 if篩選]
print(l2)
# [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99]

# 列表推導式

# (1) 過濾掉長度小於3的字串列表,並將剩下的轉換成大寫字母
lst = [`hello`, `world`, `hh`, `old`, `boy`, `H`]
l1 = [i.upper() for i in lst if len(i) >= 3]
print(1, l1)


# (2) 求(x,y)其中x是0-5之間的偶數,y是0-5之間的奇陣列成的元祖列表

l2 = [(x, y) for x in range(5) if x % 2 == 0 for y in range(5) if y % 2 == 1]
# l2 = list(zip([x for x in range(6) if x % 2 == 0], [y for y in range(6) if y % 2 == 1]))
print(2, l2)


# (3) 求M中3,6,9組成的列表M = [[1,2,3],[4,5,6],[7,8,9]]

M = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
l3 = [el[2] for el in M]
print(3, l3)


# (4) 求出50以內能被3整除的數的平方,並放入到一個列表中。

l4 = [i*i for i in range(1, 50) if i % 3 == 0]
print(4, l4)
l4_1 = [i for i in range(1, 50) if i % 3 == 0 and i*i < 50]
print(l4_1)


# (6) 構建一個列表:[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]
# l6 = [(x, y) for x in range(6) for y in range(1, 7)]
l6 = [(x, x+1) for x in range(6)]
print(6, l6)


# (7) 構建一個列表:[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
l7 = [i for i in range(0, 19, 2)]
print(7, l7)


# (8)有一個列表l1 = [`alex`, `WuSir`, `老男孩`, `太白`]將其構造成這種列表[`alex0`, `WuSir1`, `老男孩2`, `太白3`]

ll = [`alex`, `WuSir`, `老男孩`, `太白`]
l8 = [v+str(i) for i, v in enumerate(ll)]
print(8, l8)
# l8 = []
# for i,v in enumerate(ll):
#     v = v+str(i)
#     print(v,i)


# (9)有以下資料型別:
x = {
    `name`: `alex`,
    `Values`: [
        {`timestamp`:1517991992.94,
         `values`:100,},
        {`timestamp`: 1517992000.94,
        `values`: 200,},
        {`timestamp`: 1517992014.94,
         `values`: 300,},
        {`timestamp`: 1517992744.94,
         `values`: 350},
        {`timestamp`: 1517992800.94,
         `values`: 280}
], }
# 將上面的資料通過列表推導式轉換成下面的型別:
# [[1517991992.94, 100], [1517992000.94, 200], [1517992014.94, 300], [1517992744.94, 350], [1517992800.94, 280]]

l9 = [[el[`timestamp`], el[`values`]] for el in x[`Values`]]
print(9, l9)

# 結果:
1 [`HELLO`, `WORLD`, `OLD`, `BOY`]
2 [(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)]
3 [3, 6, 9]
4 [9, 36, 81, 144, 225, 324, 441, 576, 729, 900, 1089, 1296, 1521, 1764, 2025, 2304]
[3, 6]
6 [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]
7 [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
8 [`alex0`, `WuSir1`, `老男孩2`, `太白3`]
9 [[1517991992.94, 100], [1517992000.94, 200], [1517992014.94, 300], [1517992744.94, 350], [1517992800.94, 280]]
# (5) 尋找名字中帶有兩個e的人的名字
names = [[`Tom`, `Billy`, `Jefferson`, `Andrew`, `Wesley`, `Steven`,`Joe`],
 [`Alice`, `Jill`, `Ana`, `Wendy`, `Jennifer`, `Sherry`, `Eva`]]
# l5 = []
# for i in range(len(names)):
#     for j in names[i]:
#         if j.count(`e`) >= 2:
#             l5.append(j)
l5 = [j for i in range(len(names)) for j in names[i] if j.count(`e`) >= 2]
print(l5)

# 主要注意str.count()的運用,查詢字串數量

 

2,生成器表示式:

語法與列表推導式基本上是一樣的。只是把 [] 替換成 {}

gen = (i for i in range(10))  # 生成表示式
print(gen.__next__())
print(gen.__next__())
print(gen.__next__())
print(gen.__next__())
print(gen.__next__())
print(gen.__next__())
print(gen.__next__())
print(gen.__next__())
print(gen.__next__())
print(gen.__next__())
# print(gen.__next__())  # 超出數量會報錯

# 結果:
# 0
# 1
# 2
# 3
# 4
# 5
# 6
# 7
# 8
# 9

生成器表示式與列表推導式的區別:
  1,列表推導式比較耗記憶體,一次性載入;而生成器表示式幾乎不佔用記憶體,使用的時候才分配和使用記憶體。

  2,得到的值不一樣:列表推導式得到的是一個列表,生成器表示式獲得的是一個生成器。

 

生成器的惰性求值:生成器只有在訪問的時候才取值。

 

  

相關文章