1. 程式人生 > >python之生成器和列表推導式

python之生成器和列表推導式

一、生成器函式
1、生成器:就是自己用python程式碼寫的迭代器,生成器的本質就是迭代器(所以自帶了__iter__方法和__next__方法,不需要我們去實現)。
2、用以下兩種方式構建一個生成器:
1,生成器函式:跟常規函式定義類似,但是,使用yield語句而不是return語句返回結果。
yield語句一次返回一個結果,在每個結果中間,掛起函式的狀態,以便下次從它離開的地方繼續執行。

2,生成器表示式:類似於列表推導,但是,返回的是生成器的一個物件,
而不是一次構建一個結果列表。

3、生成器函式
3-1、先看一般的函式:

def func1(x):
    x += 1
    return
x func1(5) #函式的執行命令,並且接收函式的返回值。 print(func1(5)) #6

 

3-2、再看生成器函式:

複製程式碼
def func1(x):
    x += 1
    print(666)
    yield x
    x +=2
    print(777)
    print(x)
    yield 'xiaobai'
    x +=3

g = func1(5)        # 此時的func1(5)不會執行函式,因為它只是生成器函式物件
print(g)            #
<generator object func1 at 0x0000025E5D618780> print(g.__next__()) #666 6 print(next(g)) #777 8 xiaobai
複製程式碼


3-3、yield與return的區別:
return:結束函式,給函式的執行者返回值
yield:不會結束函式,一個next對應一個yield,
執行yield上面的程式碼並給 生成器物件.__next__() 返回值


3-4、生成器函式與迭代器的區別
區別1:自定製的區別
迭代器由可迭代物件轉化而來,已經‘寫死了’

l1 = [1,2,3,4,5]
l1.
__iter__()

生成器可用自定製函式來定製

複製程式碼
def func1(x):
    x += 1
    yield x
    x += 3
    yield x
    x += 5
    yield x
g1 = func1(5)
print(g1.__next__())
print(g1.__next__())
print(g1.__next__())
複製程式碼


區別2:記憶體級別的區別。
迭代器是需要可迭代物件進行轉化,可迭代物件非常佔記憶體。
生成器直接建立,不需要轉化,從本質就節省記憶體。

複製程式碼
def func1():
    for i in range(1000000):
        yield i
g1 = func1()
for i in range(50):
    print(g1.__next__()) #一個next取一次值,可用for迴圈取值
複製程式碼

 

 

3-5、send與next
先看例子:

複製程式碼
def func1():
    print(1)
    count = yield 6
    
    print(count)
    print(2)
    count1 = yield 7
    
    print(count1)
    print(3)
    yield 8

g = func1()
print(g.__next__())       #1 6
print(g.send('xiaobai'))  #xiaobai 2 7
print(g.send('xiaigou'))  #xiaogou 3 8
複製程式碼

總結:
send與next一樣,也是對生成器取值(執行一個yield)的方法。
send可以給上一個yield 傳值。
注意小坑:
1,第一次取值只能用next
2,最後一個yield不可能得到send傳的值

 

 

3-6、生成器close()方法:

複製程式碼
def fun():
    for i in range(5):
        yield i
g = fun()
print(g.__next__())
print(g.__next__())
print(g.__next__())
g.close() # 直接把生成器的值取(刪)完了,後面就不能再取值
print(g.__next__()) # 報錯


a = (i for i in range(4))
print(a.__next__())
print(a.__next__())
a.close()
print(a.__next__()) #報錯
複製程式碼

 

 

3-7、生成器函式的應用例子
要製作一批量很大的衣服,用普通的函式只能一次全部製作完:

def cloth1(n):
    for i in range(n+1):
        print('衣服%s號' % i)
cloth1(100000)  #一次全部製作完了

而用生成器函式,需要製作多少就先製作多少:

複製程式碼
def cloth2(n):
    for i in range(1,n+1):
        yield '衣服%s號' % i
g = cloth2(10000)

先製作50件:
for i in range(50):
    print(g.__next__()) #衣服1號,衣服2號,衣服3號...衣服50號

再製作50件:
for i in range(50):
    print(g.__next__()) #衣服51號,衣服52號,衣服53號...衣服100號
複製程式碼

 

 


二、列表推導式,生成器表示式
1、普通方法建立一個元素為1到100的列表:

l1 = []
for num in range(1,101):
    l1.append(num)
print(l1)

 

 

2、列表推導式:一行程式碼幾乎搞定你需要的任何的列表。
2-1、兩種方式:
  迴圈模式
  篩選模式

2-2、迴圈模式:[變數(加工後的變數) for 變數 in iterable]

複製程式碼
#1到100的列表
l = [i for i in range(1,101)]
print(l) #[1,2,3,4...100]

#python1到python15的列表
l2 = ['python%s' % i for i in range(1,16)]
print(l2)  #[python1,python2,python3...python15,]

#1到10的平方
l3 = [i*i for i in range(1,11)]
print(l3) #[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
複製程式碼

 

 

2-3、篩選模式 [變數(加工後的變數) for 變數 in iterable if 條件]

複製程式碼
#30以內的偶數
l4 = [i for i in range(1,31) if i % 2 == 0]
print(l4) #[2, 4, 6, 8,10,...28, 30]

#30以內能被3整除的數
l5 = [i for i in range(1,31) if i % 3 == 0]
print(l5) #[3, 6, 9, 12, 15, 18, 21, 24, 27, 30]

#30以內能被3整除的數的平方
l6 = [i**2 for i in range(1,31) if i % 3 == 0]
print(l6) #[9, 36, 81, 144, 225, 324, 441, 576, 729, 900]


#找出列表中含有兩個'e'的元素
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
         ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
l7 = [j for i in names for j in i if j.count('e') == 2]
print(l7) #['Jefferson', 'Wesley', 'Steven', 'Jennifer']
複製程式碼

 

 

2-4、列表推導式優缺點
優點:一行解決,方便。
缺點:容易著迷,不易排錯,不能超過三次迴圈。
列表推導式不能解決所有列表的問題,所以不要太刻意用。

3、生成器表示式:將列表推導式的[]換成()即可。

複製程式碼
g = (i for i in range(100000000000))
print(g) #生成器表示式左邊的變數g是生成器物件
print(g.__next__())  # 0
print(g.__next__())  # 1
print(g.__next__())  # 2

上面程式碼相當於:
def func():
    for i in range(100000000000):
        yield i
g = func()  #生成器物件
print(g.__next__())
print(g.__next__())
print(g.__next__())
複製程式碼

 

 

4、擴充套件
4-1、字典推導式

複製程式碼
# 例一:將一個字典的key和value對調
dic = {'a': 10, 'b': 34}
new_dic = {dic[k]: k for k in dic}
print(new_dic)
# 結果:
# {10: 'a', 34: 'b'}


# 例二:合併大小寫對應的value值,將k統一成小寫
dic = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
new_dic = {k.lower():dic.get(k.lower(),0) + dic.get(k.upper(),0) for k in dic}
print(new_dic)
# 結果:
# {'a': 17, 'b': 34, 'z': 3}
複製程式碼

 

 

 

4-2、集合推導式

# 例一:計算列表中每個值的平方,自帶去重功能
set1 = {x**2 for x in [1, -1, 2]}
print(set1)
# 結果:
# {1, 4}

 

 

 

4-3、集合推導式和字典推導式的區別

複製程式碼
相同點:外層都是使用大括號{}
不同點:返回的形式是 key:values 形式的就是字典,返回的形式是 values1,values2...形式的是集合
# 集合推導式
set1 = {x**2 for x in (1, -1, 2)}
print(set1,type(set1))  # {1, 4} <class 'set'>
 

# 字典推導式
dic1 = {x**2:x for x in (1, -1, 2)}
print(dic1,type(dic1))  # {1: -1, 4: 2} <class 'dict'>
複製程式碼

 

 

4-4、練習題

 

複製程式碼
# 例1:  過濾掉長度小於3的字串列表,並將剩下的轉換成大寫字母
l1 = [i.upper() for i in ['asdf','a','b','qwe'] if len(i) >= 3]
print(l1)
# 結果:
# ['ASDF', 'QWE']

# 例2:  求(x,y)其中x是0-5之間的偶數,y是0-5之間的奇陣列成的元組列表
l2 = [(x,y) for x in range(6) if x % 2 ==0 for y in range(6) if y % 2 == 1]
print(l2)
# 結果:
# [(0, 1), (0, 3), (0, 5), (2, 1), (2, 3), (2, 5), (4, 1), (4, 3), (4, 5)]


# 例3:  求M中3,6,9組成的列表
M = [[1,2,3],[4,5,6,24,3],[7,8,9,12,9]]
l3 = [j for i in M for j in i for z in [3,6,9] if j / z == 1 ]
print(l3)
# 結果:
# [3, 6, 3, 9, 9]
複製程式碼

 

 

 

 

 

一、生成器函式
1、生成器:就是自己用python程式碼寫的迭代器,生成器的本質就是迭代器(所以自帶了__iter__方法和__next__方法,不需要我們去實現)。
2、用以下兩種方式構建一個生成器:
1,生成器函式:跟常規函式定義類似,但是,使用yield語句而不是return語句返回結果。
yield語句一次返回一個結果,在每個結果中間,掛起函式的狀態,以便下次從它離開的地方繼續執行。

2,生成器表示式:類似於列表推導,但是,返回的是生成器的一個物件,
而不是一次構建一個結果列表。

3、生成器函式
3-1、先看一般的函式:

def func1(x):
    x += 1
    return x
func1(5) #函式的執行命令,並且接收函式的返回值。
print(func1(5))  #6

 

3-2、再看生成器函式:

複製程式碼
def func1(x):
    x += 1
    print(666)
    yield x
    x +=2
    print(777)
    print(x)
    yield 'xiaobai'
    x +=3

g = func1(5)        # 此時的func1(5)不會執行函式,因為它只是生成器函式物件
print(g)            # <generator object func1 at 0x0000025E5D618780>
print(g.__next__()) #666 6
print(next(g))      #777 8 xiaobai
複製程式碼


3-3、yield與return的區別:
return:結束函式,給函式的執行者返回值
yield:不會結束函式,一個next對應一個yield,
執行yield上面的程式碼並給 生成器物件.__next__() 返回值


3-4、生成器函式與迭代器的區別
區別1:自定製的區別
迭代器由可迭代物件轉化而來,已經‘寫死了’

l1 = [1,2,3,4,5]
l1.__iter__()

生成器可用自定製函式來定製

複製程式碼
def func1(x):
    x += 1
    yield x
    x += 3
    yield x
    x += 5
    yield x
g1 = func1(5)
print(g1.__next__())
print(g1.__next__())
print(g1.__next__())
複製程式碼


區別2:記憶體級別的區別。
迭代器是需要可迭代物件進行轉化,可迭代物件非常佔記憶體。
生成器直接建立,不需要轉化,從本質就節省記憶體。

複製程式碼
def func1():
    for i in range(1000000):
        yield i
g1 = func1()
for i in range(50):
    print(g1.__next__()) #一個next取一次值,可用for迴圈取值
複製程式碼

 

 

3-5、send與next
先看例子:

複製程式碼
def func1():
    print(1)
    count = yield 6
    
    print(count)
    print(2)
    count1 = yield 7
    
    print(count1)
    print(3)
    yield 8

g = func1()
print(g.__next__())       #1 6
print(g.send('xiaobai'))  #xiaobai 2 7
print(g.send('xiaigou'))  #xiaogou 3 8
複製程式碼

總結:
send與next一樣,也是對生成器取值(執行一個yield)的方法。
send可以給上一個yield 傳值。
注意小坑:
1,第一次取值只能用next
2,最後一個yield不可能得到send傳的值

 

 

3-6、生成器close()方法:

複製程式碼
def fun():
    for i in range(5):
        yield i
g = fun()
print(g.__next__())
print(g.__next__())
print(g.__next__())
g.close() # 直接把生成器的值取(刪)完了,後面就不能再取值
print(g.__next__()) # 報錯


a = (i for i in range(4))
print(a.__next__())
print(a.__next__())
a.close()
print(a.__next__()) #報錯
複製程式碼

 

 

3-7、生成器函式的應用例子
要製作一批量很大的衣服,用普通的函式只能一次全部製作完:

def cloth1(n):
    for i in range(n+1):
        print('衣服%s號' % i)
cloth1(100000)  #一次全部製作完了

而用生成器函式,需要製作多少就先製作多少:

複製程式碼
def cloth2(n):
    for i in range(1,n+1):
        yield '衣服%s號' % i
g = cloth2(10000)

先製作50件:
for i in range(50):
    print(g.__next__()) #衣服1號,衣服2號,衣服3號...衣服50號

再製作50件:
for i in range(50):
    print(g.__next__()) #衣服51號,衣服52號,衣服53號...衣服100號
複製程式碼

 

 


二、列表推導式,生成器表示式
1、普通方法建立一個元素為1到100的列表:

l1 = []
for num in range(1,101):
    l1.append(num)
print(l1)

 

 

2、列表推導式:一行程式碼幾乎搞定你需要的任何的列表。
2-1、兩種方式:
  迴圈模式
  篩選模式

2-2、迴圈模式:[變數(加工後的變數) for 變數 in iterable]

複製程式碼
#1到100的列表
l = [i for i in range(1,101)]
print(l) #[1,2,3,4...100]

#python1到python15的列表
l2 = ['python%s' % i for i in range(1,16)]
print(l2)  #[python1,python2,python3...python15,]

#1到10的平方
l3 = [i*i for i in range(1,11)]
print(l3) #[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
複製程式碼

 

 

2-3、篩選模式 [變數(加工後的變數) for 變數 in iterable if 條件]

複製程式碼
#30以內的偶數
l4 = [i for i in range(1,31) if i % 2 == 0]
print(l4) #[2, 4, 6, 8,10,...28, 30]

#30以內能被3整除的數
l5 = [i for i in range(1,31) if i % 3 == 0]
print(l5) #[3, 6, 9, 12, 15, 18, 21, 24, 27, 30]

#30以內能被3整除的數的平方
l6 = [i**2 for i in range(1,31) if i % 3 == 0]
print(l6) #[9, 36, 81, 144, 225, 324, 441, 576, 729, 900]


#找出列表中含有兩個'e'的元素
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
         ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
l7 = [j for i in names for j in i if j.count('e') == 2]
print(l7) #['Jefferson', 'Wesley', 'Steven', 'Jennifer']
複製程式碼

 

 

2-4、列表推導式優缺點
優點:一行解決,方便。
缺點:容易著迷,不易排錯,不能超過三次迴圈。
列表推導式不能解決所有列表的問題,所以不要太刻意用。

3、生成器表示式:將列表推導式的[]換成()即可。

複製程式碼
g = (i for i in range(100000000000))
print(g) #生成器表示式左邊的變數g是生成器物件
print(g.__next__())  # 0
print(g.__next__())  # 1
print(g.__next__())  # 2

上面程式碼相當於:
def func():
    for i in range(100000000000):
        yield i
g = func()  #生成器物件
print(g.__next__())
print(g.__next__())
print(g.__next__())
複製程式碼

 

 

4、擴充套件
4-1、字典推導式

複製程式碼
# 例一:將一個字典的key和value對調
dic = {'a': 10, 'b': 34}
new_dic = {dic[k]: k for k in dic}
print(new_dic)
# 結果:
# {10: 'a', 34: 'b'}


# 例二:合併大小寫對應的value值,將k統一成小寫
dic = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
new_dic = {k.lower():dic.get(k.lower(),0) + dic.get(k.upper(),0) for k in dic}
print(new_dic)
# 結果:
# {'a': 17, 'b': 34, 'z': 3}
複製程式碼

 

 

 

4-2、集合推導式

# 例一:計算列表中每個值的平方,自帶去重功能
set1 = {x**2 for x in [1, -1, 2]}
print(set1)
# 結果:
# {1, 4}

 

 

 

4-3、集合推導式和字典推導式的區別

複製程式碼
相同點:外層都是使用大括號{}
不同點:返回的形式是 key:values 形式的就是字典,返回的形式是 values1,values2...形式的是集合
# 集合推導式
set1 = {x**2 for x in (1, -1, 2)}
print(set1,type(set1))  # {1, 4} <class 'set'>
 

# 字典推導式
dic1 = {x**2:x for x in (1, -1, 2)}
print(dic1,type(dic1))  # {1: -1, 4: 2} <class 'dict'>
複製程式碼

 

 

4-4、練習題

 

複製程式碼
# 例1:  過濾掉長度小於3的字串列表,並將剩下的轉換成大寫字母
l1 = [i.upper() for i in ['asdf','a','b','qwe'] if len(i) >= 3]
print(l1)
# 結果:
# ['ASDF', 'QWE']

# 例2:  求(x,y)其中x是0-5之間的偶數,y是0-5之間的奇陣列成的元組列表
l2 = [(x,y) for x in range(6) if x % 2 ==0 for y in range(6) if y % 2 == 1]
print(l2)
# 結果:
# [(0, 1), (0, 3), (0, 5), (2, 1), (2, 3), (2, 5), (4, 1), (4, 3), (4, 5)]


# 例3:  求M中3,6,9組成的列表
M = [[1,2,3],[4,5,6,24,3],[7,8,9,12,9]]
l3 = [j for i in M for j in i for z in [3,6,9] if j / z == 1 ]
print(l3)
# 結果:
# [3, 6, 3, 9, 9]
複製程式碼