1. 程式人生 > >python全棧開發-前方高能-生成器和生成器表示式 python_day_13

python全棧開發-前方高能-生成器和生成器表示式 python_day_13

今日主要內容 1. 生成器和生成器函式 生成器的本質就是迭代器 生成器的三種建立辦法:

  • 1.通過生成器函式
  • 2.通過生成器表示式建立生成器
  • 3.通過資料轉換

生成器函式: 函式中包含了yield的就是生成器函式 注意:生成器函式被執行. 獲取到的是生成器. 而不是函式的執行 生成器表示式: (結果 for 變數 in 可迭代物件 if 篩選) 取值:

  • 1. __next__()
  • 2. send(值) 給上一個yield位置傳一個值, 第一個和最後一個yield不用傳值
  • 3. 可以for迴圈
  • 4. list(g)
  • 2. 各種推導式和生成器表示式
  • 1. 列表推導式 [結果 for 變數 in 可迭代物件 if 篩選]
  • 2. 字典推導式 {結果 for 變數 in 可迭代物件 if 篩選} 結果=>key:value
  • 3. 集合推導式 {結果 for 變數 in 可迭代物件 if 篩選} 結果=>key

12. 前⽅⾼能-⽣成器和⽣成器表示式 

本節主要內容: 

  • 1. ⽣成器和⽣成器函式 
  • 2. 列表推導式

⼀. ⽣成器 什麼是⽣成器. ⽣成器實質就是迭代器. 

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

運⾏的結果和上⾯不⼀樣. 為什麼呢. 由於函式中存在了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__()
print(ret3)# 最後⼀個yield執⾏完畢. 再次__next__()程式報錯, 也就是說. 和return⽆關了.

結果:
111
Traceback (most recent call last):
222
  File "D:/pycodes/第13天/day13.py", line 117, in <module>
333
    ret3 = gener.__next__() # 最後⼀個yield執⾏完畢. 再次__next__()程式報錯, 也就是說. 和return⽆關了.
444
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

⼆. 列表推導式, ⽣成器表示式以及其他推導式 ⾸先我們先看⼀下這樣的程式碼, 給出⼀個列表, 通過迴圈, 向列表中新增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.獲取1-100內能被3整除的數
lst = [i for i in range(1,101) if i%3==0]
print(lst)
# 2.100以內能被3整除的數的平⽅
lst = [i*i for i in range(1,101) if i%3==0]
print(lst)
# 3.尋找名字中帶有兩個e的⼈的名字
names = [['Tom', 'Billy', 'Jefferson' , 'Andrew' , 'Wesley' , 'Steven' ,'Joe'],['Alice', 'Jill' , 'Ana', 'Wendy', 'Jennifer', 'Sherry' , 'Eva']]
lst = [a for i in names for a in i if a.count('e')==2]
print(lst)

⽣成器表示式和列表推導式的區別:

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迴圈.⽣成器具有 惰性機制.

2018-07-18  15:45:41

=============================7.20更新=============================

作業:

2,用列表推導式做下列小題
(1)過濾掉長度小於3的字串列表,並將剩下的轉換成大寫字母
lst = ['asas','a','aa','adas','ac','adc']
lis = [i.upper() for i in lst if len(i)>3]
print(lis)
(2)求(x,y)其中x是0-5之間的偶數,y是0-5之間的奇陣列成的元祖列表
lst = [(x,y) for x in range(6) for y in range(6) if x%2==0 and y%2==1]
print(lst)
(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]]
lst = [ a for i in M for a in i if a ==i[2]]
print(lst)
(4)求出50以內能被3整除的數的平方,並放入到一個列表中。
lst = [i*i for i in range(51) if i%3==0]
print(lst)
(5)構建一個列表:['python1期', 'python2期', 'python3期', 'python4期', 'python6期', 'python7期', 'python8期', 'python9期', 'python10期']
lst = ['python%s期' % i for i in range(1,11) ]
print(lst)
(6)構建一個列表:[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]
lst = [(x,y) for x in range(6) for y in range(1,7) if y==x+1]
print(lst)
(7)構建一個列表:[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
lst = [i for i in range(19) if i%2==0]
print(lst)
(8)有一個列表l1 = ['alex', 'WuSir', '老男孩', '太白']將其構造成這種列表['alex0', 'WuSir1', '老男孩2', '太白3']
l1 = ['alex', 'WuSir', '老男孩', '太白']
lst =[l1[a]+str(a) for a in range len(l1) ]
print(lst)
(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]]
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}
               ], }

lst = [[i['timestamp'], i['values']] for i in x["Values"]]
print(lst)