1. 程式人生 > 實用技巧 >推導式與生成器

推導式與生成器

一、列表推導式

'''通過一行迴圈判斷,遍歷一系資料的方式'''
推導式語法
    val for val in Iterable
    三種方式:
                [val for val in Iterable]
                {val for val in Iterable}
                {k:v for k,v in Iterable}        

1、向列表裡插入100條資料

#列表裡面需要100條資料
lst = []
for i in range(1,101):
    lst.append(i)
print(lst)

改為推導式

# 基本語法
lst = [i for i in range(1,101)]
print(lst)

2、[1,2,3,4,5] -> [3,6,9,12,15]

lst = [1,2,3,4,5]
lst_new = []
for i in lst:
    res = i * 3
    lst_new.append(res)
print(lst_new)

改成推導式

lst = [i*3 for i in lst]
print(lst)

3、帶有判斷條件的單迴圈推導式 (只能是單項分支,接在for後面)

lst = [1,2,3,4,5,6,7,8]

lst_new 
= [] for i in lst:   if i %2 ==1:     lst_new.append(i) print(lst_new )

改寫成推導式

lst = [i for i in lst if i %2 ==1]
print(lst)

4、雙迴圈推導式

lst1 = ["李博倫","高雲峰","孫致和","葛龍"]
lst2 = ["李亞","劉彩霞","劉子豪","劉昕"]
# "誰"❤"誰"
lst_new = []
for i in lst1:
    for j in lst2:
        strvar = i+ '*' + j
        lst_new.append(strvar)

print(lst_new)

改寫成推導式

# 改寫成推導式
lst = [i+'*'+j for i in lst1 for j in lst2]
print(lst)

5、帶有判斷條件的多迴圈推導式

lst_new = []
for i in lst1:
    for j in lst2:
        if lst1.index(i) == lst2.index(j)
            strvar = i + '*' + j
            lst_new.append(strvar)

print(lst_new)

改寫成推導式

lst = [ i + "" + j for i in lst1 for j in lst2 if lst1.index(i) == lst2.index(j) ]
print(lst)

二、集合推導式

"""
案例:
    滿足年齡在18到21,存款大於等於5000 小於等於5500的人,
    開卡格式為:尊貴VIP卡老x(姓氏),否則開卡格式為:摳腳大漢卡老x(姓氏)    
    把開卡的種類統計出來
"""
listvar = [
    {"name":"劉鑫煒","age":18,"money":10000},
    {"name":"劉聰","age":19,"money":5100},
    {"name":"劉子豪","age":20,"money":4800},
    {"name":"孔祥群","age":21,"money":2000},
    {"name":"宋雲傑","age":18,"money":20}
]

常規寫法

setvar = set()
for i in listvar:
    if 18 <= i["age"] <= 21 and  5000 <= i["money"] <= 5500:
        res = "尊貴VIP卡老" + i["name"][0]
    else:
        res = "摳腳大漢卡老" + i["name"][0]
    setvar.add(res)
print(setvar)

改寫成集合推導式

# {三元運算子 + 推導式}
setvar = { "尊貴VIP卡老" + i["name"][0] if 18 <= i["age"] <= 21 and  5000 <= i["money"] <= 5500 else "摳腳大漢卡老" + i["name"][0] for i in listvar }
print(setvar)

三、字典推導式

"""
enumerate(iterable,[start=0])
功能:列舉 ; 將索引號和iterable中的值,一個一個拿出來配對組成元組放入迭代器中
引數:
    iterable: 可迭代性資料 (常用:迭代器,容器型別資料,可迭代物件range) 
    start:  可以選擇開始的索引號(預設從0開始索引)
返回值:迭代器
"""
from collections import Iterator
lst = ["東邪","西毒","南帝","北丐"]

# 基本使用
it = enumerate(lst)
print(isinstance(it,Iterator))

for + next

# for + next
for i in range(4):
    print(next(it))

# (0, '東邪')
# (1, '西毒')
# (2, '南帝')
# (3, '北丐')

list

"""start可以指定開始值,預設是0"""
it = enumerate(lst,start=1)
print(list(it))

#[(1, '東邪'), (2, '西毒'), (3, '南帝'), (4, '北丐')]

enumerate 形成字典推導式 變成字典

dic = { k:v for k,v in enumerate(lst,start=1) }
print(dic)

# {1: '東邪', 2: '西毒', 3: '南帝', 4: '北丐'}

dict 強制變成字典

dic = dict(enumerate(lst,start=1))
print(dic)
# {1: '東邪', 2: '西毒', 3: '南帝', 4: '北丐'}

四、zip

"""
zip(iterable, ... ...)
    功能: 將多個iterable中的值,一個一個拿出來配對組成元組放入迭代器中
    iterable: 可迭代性資料 (常用:迭代器,容器型別資料,可迭代物件range) 
返回: 迭代器

特徵: 如果找不到對應配對的元素,當前元素會被捨棄
"""
# 基本使用
lst1 = ["晏國彰","劉子濤","郭凱","宋雲傑"]
lst2 = ["劉有右柳翔","馮雍","孫志新"]
lst3 = ["周鵬飛","袁偉倬"]
# it = zip(lst1,lst2)
it = zip(lst1,lst2,lst3)
print(isinstance(it,Iterator))
print(list(it))
"""
[('晏國彰', '劉有右柳翔'), ('劉子濤', '馮雍'), ('郭凱', '孫志新')]
[('晏國彰', '劉有右柳翔', '周鵬飛'), ('劉子濤', '馮雍', '袁偉倬')]
"""

1、zip 形成字典推導式 變成字典

lst1 = ["晏國彰","劉子濤","郭凱","宋雲傑"]
lst2 = ["劉有右柳翔","馮雍","孫志新"]
dic = { k:v for k,v in zip(lst1,lst2) }
print(dic)

# dict 強制變成字典
dic = dict(zip(lst1,lst2))
print(dic)

五、生成器表示式

"""
#生成器本質是迭代器,允許自定義邏輯的迭代器

#迭代器和生成器區別:
    迭代器本身是系統內建的.重寫不了.而生成器是使用者自定義的,可以重寫迭代邏輯

#生成器可以用兩種方式建立:
    (1)生成器表示式  (裡面是推導式,外面用圓括號)
    (2)生成器函式    (用def定義,裡面含有yield)
"""
from collections import Iterator,Iterable
# 生成器表示式
gen = (i*2 for i in range(1,11))
print(isinstance(gen,Iterator))

# next 
res = next(gen)
print(res)

# for 
for i in gen:
    print(i)

# for + next
gen = (i*2 for i in range(1,11))
for i in range(3):
    res = next(gen)
    print(res)

# list
print("<=====>")
res = list(gen)
print(res)

六、生成器函式

"""
# yield 類似於 return
共同點在於:執行到這句話都會把值返回出去
不同點在於:yield每次返回時,會記住上次離開時執行的位置 , 下次在呼叫生成器 , 會從上次執行的位置往下走
           而return直接終止函式,每次重頭呼叫.
yield 6 和 yield(6) 2種寫法都可以 yield 6 更像 return 6 的寫法 推薦使用
"""

1、生成器函式的基本語法

# 定義一個生成器函式
def mygen():
    print(111)
    yield 1
    
    print(222)
    yield 2
    
    print(333)
    yield 3

# 初始化生成器函式,返回生成器物件,簡稱生成器
gen = mygen()
print(isinstance(gen,Iterator))

# 使用next呼叫
res = next(gen)
print(res)
res = next(gen)
print(res)
res = next(gen)
print(res)
# res = next(gen) error
# print(res)

2、程式碼優化

def mygen():
    for i in range(1,101):
        yield "該球衣號碼是{}".format(i)
# 初始化生成器函式 -> 生成器        
gen = mygen()

# for + next 呼叫資料
for i in range(50):
    res = next(gen)
    print(res)
print("<====>")
for i in range(30):
    res = next(gen)
    print(res)

3、send用法\

"""
### send
# next和send區別:
    next 只能取值
    send 不但能取值,還能傳送值
# send注意點:
    第一個 send 不能給 yield 傳值 預設只能寫None
    最後一個yield 接受不到send的傳送值
    send 是給上一個yield傳送值    
"""

def mygen():
    print("process start")
    res = yield 100
    print(res,"內部列印1")
    
    res = yield 200
    print(res,"內部列印2")
    
    res = yield 300
    print(res,"內部列印3")
    print("process end")

# 初始化生成器函式 -> 生成器
gen = mygen()
# 在使用send時,第一次呼叫必須傳遞的引數是None(硬性語法),因為第一次還沒有遇到上一個yield
'''第一次呼叫'''
res = gen.send(None) #<=> next(gen)
print(res)
'''第二次呼叫'''
res = gen.send(101) #<=> next(gen)
print(res)
'''第三次呼叫'''
res = gen.send(201) #<=> next(gen)
print(res)
'''第四次呼叫, 因為沒有更多的yield返回資料了,所以StopIteration'''

4、yield from : 將一個可迭代物件變成一個迭代器返回

def mygen():
    yield from ["馬生平","劉彩霞","餘銳","晏國彰"]
    
gen = mygen()
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))

5、用生成器描述斐波那契數列

"""1 1 2 3 5 8 13 21 34 ... """
"""
yield 1
a,b = b,a+b = 1,1

yield 1
a,b = b,a+b = 1,2

yield 2
a,b = b,a+b = 2,3

yield 3
a,b = b,a+b = 3,5

yield 5
....

"""

def mygen(maxlen):
    a,b = 0,1
    i = 0
    while i < maxlen:
        yield b
        a,b = b,a+b
        i+=1
    
# 初始化生成器函式 -> 生成器
gen = mygen(10)

for i in range(3):
    print(next(gen))