python基礎學習筆記——生成器與推導式
生成器
首先我們來看看什麼是個生成器,生成器本質就是迭代器
在python中有三種方式來獲取生成器
1.通過生成器函式
2.通過各種推到式來實現生成器
3.通過資料的轉換也可以獲取生成器
首先,我們先看一個很簡單的函式:
1 2 3 4 5 6 7 8 9 10 11 |
def
func():
print
(
11
)
return
22
ret =
func()
print
(ret)
# 執行結果:
11
22
|
將函式中的return換成yield就是生成器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# 函式
def func():
print(
'這是函式func'
)
return
'函式func'
func()
# 生成器
def func1():
print(
'這是函式func1'
)
yield
'函式func'
func1()
|
執行的結果和上面的不一樣,為什麼呢?? 由於函式中存在yield,那麼這個函式就是一個生成器函式.
1 2 3 4 5 6 7 8 9 |
def func1():
print(
'這是函式func1'
)
yield
'函式func'
print(func1())
結果:<generator
object
func1 at 0x0000023B3F280B48>
|
我們在執行這個函式的時候.就不再是函式的執行了.而是獲取這個生成器.如何使用???
想想迭代器,生成器的本質就是迭代器.所以我們可以直接執行__next__()來執行以下生成器
1 2 3 4 5 6 7 8 9 |
def
func():
print
(
"111"
)
yield
222
gener
=
func()
# 這個時候函式不會執⾏. ⽽是獲取到⽣成器
ret
=
gener.__next__()
# 這個時候函式才會執⾏. yield的作⽤和return⼀樣. 也是返回資料
print
(ret)
結果:
111
222
|
那麼我們可以看到,yield和return的效果是一樣的,但是還是有點區別
yield是分段來執行一個函式
return是直接停止這個函式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
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__()程式會報錯
好了生成器我們說完了.生成器有什麼作用呢?
我們來看一下這個需求,老男孩向樓下賣包子的老闆訂購了10000個包子.包子鋪老闆實在一下就全部都做出來了
1 2 3 4 5 6 7 8 9 10 |
def
eat():
lst
=
[]
for
i
in
range
(
1
,
10000
):
lst.append(
'包子'
+
str
(i))
return
lst
e
=
eat()
print
(e)
|
這樣做是沒有問題但是我們目前這麼點人吃不完這麼多,只能先放到一個地方,要是能夠我吃一個老闆做一個就完美了.
1 2 3 4 5 6 7 8 9 10 |
def
eat():
for
i
in
range
(
1
,
10000
):
yield
'包子'
+
str
(i)
e
=
eat()
print
(e.__next__())
print
(e.__next__())
print
(e.__next__())
print
(e.__next__())
print
(e.__next__())
print
(e.__next__())
|
上下的區別: 第一種是直接把包子都拿來,很佔記憶體也就是很佔咱們的位置,第二種使用生成器,想吃就拿一個.吃多少個包多少個.生成器是一個一個的,一直向下進行,不能向上.__next__()到哪,指標就指到哪兒.下一次繼續就獲取指標指向的值
接下來我們再來認識一個新的東西,send方法
send和__next__()一樣都可以讓生成器執行到下一個yield
1 2 3 4 5 6 7 8 9 10 11 |
def
eat():
for
i
in
range
(
1
,
10000
):
a
=
yield
'包子'
+
str
(i)
print
(
'a is'
,a)
b
=
yield
'窩窩頭'
print
(
'b is'
, b)
e
=
eat()
print
(e.__next__())
print
(e.send(
'大蔥'
))
print
(e.send(
'大蒜'
))
|
send和__next__()區別:
send 和 next()都是讓生成器向下走一次
send可以給上一個yield的位置傳遞值,不能給最後一個yield傳送值,在第一次執行生成器的時候不能使用send()
第一次呼叫的時候使用send()也可以但是send的引數必須是None
1 2 3 4 5 6 7 8 9 |
def func1():
print(
'這是函式func1'
)
f1 = yield
'你好'
print(f1)
f2 = yield
'我好'
print(f2)
f = func1()
f.__next__()
f.send(
'大家好'
)
|
生成器可以for迴圈來迴圈獲取內部元素:
1 2 3 4 5 6 7 8 9 10 11 |
def
func():
yield
1
yield
2
yield
3
yield
4
yield
5
f
=
func()
for
i
in
f:
print
(i)
|
yield from
在python3中提供一種可以直接把可迭代物件中的每一個數據作為生成器的結果進行返回
1 2 3 4 5 6 |
def func():
lst = [
'衛龍'
,
'老冰棍'
,
'北冰洋'
,
'牛羊配'
]
yield
from
lst
g = func()
for
i
in
g:
print(i)
|
有個小坑,yield from 是將列表中的每一個元素返回,所以 如果寫兩個yield from 並不會產生交替的效果
1 2 3 4 5 6 7 8 9 |
def func():
lst1 = [
'衛龍'
,
'老冰棍'
,
'北冰洋'
,
'牛羊配'
]
lst2 = [
'饅頭'
,
'花捲'
,
'豆包'
,
'大餅'
]
yield
from
lst1
yield
from
lst2
g = func()
for
i
in
g:
print(i)
|
推導式
列表推導式
列表推導式生成器表示式以及其他推導式,首先我們先看一下這樣的程式碼,給出一個列表,通過迴圈,想列表中新增1~10:
1 2 3 4 5 |
li
=
[]
for
i
in
range
(
10
):
li.append(i)
print
(li)
|
我們換成列表推導式是什麼樣的,來看看:
列表推導式的常⽤寫法:
[結果 for 變數 in 可迭代物件]
1 2 |
ls
=
[i
for
i
in
range
(
10
)]
print
(ls)
|
列表推導式是通過⼀行來構建你要的列表, 列表推導式看起來程式碼簡單. 但是出現錯誤之
後很難排查.
例. 從python1期到python17期寫入列表lst:
1 2 |
lst
=
[
'python%s'
%
i
for
i
in
range
(
1
,
18
)]
print
(lst)
|
篩選模式
[結果 for 變數 in 可迭代物件 if 條件]
1 2 3 |
print([i
for
i
in
range(10)
if
i > 3])
結果:
[4, 5, 6, 7, 8, 9]
|
生成器表示式
這個其實就將列表推導式倆邊的中括號換成小括號就可以了,我們來看一下
1 2 3 4 5 6 7 8 |
l = (i
for
i
in
range(10))
print(l)
print(l.__next__())
print(l.__next__())
print(l.__next__())
print(l.__next__())
print(l.__next__())
|
print(l)的時候獲取到是:
1 2 3 4 5 6 |
<generator
object
<genexpr> at 0x000001D8C7570B48>
0
1
2
3
4
|
生成器表示式也可以進行篩選
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# 獲取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
i
in
gen:
print(i)
|
生成器表示式和列表推導式的區別:
1. 列表推導式比較耗記憶體,一次性載入.生成器表示式幾乎不佔用記憶體.使用的時候才分配和使用記憶體
2. 得到的值不一樣,列表推導式得到的是一個列表.生成器表示式獲取的是一個生成器
舉個例子:
李大錘想吃雞蛋就上街買了一籃子的雞蛋放家裡,吃的時候拿一個吃的時候拿一個,這樣就是一個列表推導式,一次性拿夠佔地方.
王二麻子也想吃雞蛋,他上街卻買了一隻母雞回家.等