1. 程式人生 > >函式進階三(生成器、生成器表示式、匿名函式)

函式進階三(生成器、生成器表示式、匿名函式)

. 昨日內容回顧
函式名的運用:
1.特殊的變數
2.函式名可以當作變數賦值
3.函式名可以當作容器類型別的元素
4.函式名可以當作函式的引數
5.函式名可以當作函式的返回值

閉包:
1.內層函式對外層函式(非全域性)變數的引用和改變
2.閉包只存在於內層函式中
3.閉包都要逐層返回,最終返回給最外層函式

閉包的特點:
直譯器遇到閉包,不會隨著函式的結束而結束空間

閉包應用:
裝飾器
爬蟲

可迭代物件
判斷方法:
obj
"__iter__" in dir(obj)

from collections import Iterable
isinstance(obj, Iterable)

可迭代物件不能直接取值,必須轉化成迭代器取值(__next__)

# 迭代器
"__iter__" in dir(obj)

from collections import Iterable
isinstance(obj, Iterator)

while迴圈模擬for迴圈

. 生成器

生成器的本質就是迭代器,生成器是自己用Python程式碼寫的迭代器
生成器函式
生成器表示式

s1 = "ajlsdj"
iter(s1) # 這不是手寫,是通過Python寫的

將一個函式變成生成器函式
只要有 yield 就不是函式,可看成生成器
 
 
 
# 生成器函式

def func():
    print(111)
    print(222)
    yield 666
    # yield "aklsj"

# print(func())  # <generator object func at 0x000001AE6CB50BF8>  返回的是生成器物件
ret = func()
print(next(ret)) # 這裡一個 next 對應一個 yield # print(next(ret)) # 所以這裡會報錯 StopIteration,除非上面再加個 yield # 111 # 222 # 666
 

 

# yield 和 return 的共同點和區別

# 1. 區別:return 終止函式,而 yield 不會終止生成器函式
# 2. 共同點:都會返回一個值,return給函式的執行者返回值,yield是給next()返回值

# 示例:
def cloth():
    for i in range(1, 5001):
        
print("我寫的程式碼沒有bug 衣服%s號" % i) cloth() # 我寫的程式碼沒有bug 衣服1號 # 我寫的程式碼沒有bug 衣服2號 # 。。。 # 我寫的程式碼沒有bug 衣服5000號 def cloth1(): for i in range(1, 5001): yield "有志青年 衣服%s號" % i genor = cloth1() for i in range(1, 101): print(next(genor)) for i in range(1, 51): print(next(genor)) # 有志青年 衣服1號 # 有志青年 衣服2號 # 。。。 # 有志青年 衣服100號 # 有志青年 衣服101號 # 。。。 # 有志青年 衣服149號 # 有志青年 衣服150號 # 由列印結果可知,生成器會記錄上一次執行的位置,再次執行時從上一次位置開始計數

 

# 以下很重要,必須掌握
# send next

def func():
    yield 666
    yield "abc"
    yield "速度"
    yield "hxy"
genor = func()
print(next(genor)) # 666
print(next(genor)) # abc

print(genor.send(None)) # send() 必須要有一個引數  # 666
print(genor.send(None)) # abc

# send 不僅能對應 yield 取值,而且可以給上一個 yield 傳送一個值
def func():
    count = yield 666
    print(count)
    s = yield "abc"
    print(s)
    yield "速度"
    yield "hxy"
genor = func()
print(genor.send(None))  # 666
print(genor.send("alex"))  # alex abc
print(genor.send("111"))  # 111 速度

# 第一個取值能否用 send 傳參?
# 不能,比如上面的 print(genor.send(None)) 中的 None 改為 其他的,那麼會報錯
# 因為 send 是給上一個 yield 傳送一個值,第一個上面沒有值了

# 最後一個 yield 永遠也得不到 send 傳的值。

 

# yield from 將一個可迭代物件變成一個迭代器返回

def func():
    lst = ["abc", "def", "ghi", "jkl"]
    yield lst
genor = func()
print(next(genor))  # ['abc', 'def', 'ghi', 'jkl']
for i in genor:
    print(i)
    
# ['abc', 'def', 'ghi', 'jkl']

def func():
    lst = ["abc", "def", "ghi", "jkl"]
    yield from lst  # 注意這裡與上面的區別
genor = func()
print(next(genor))  # abc
print(next(genor))  # def
print(next(genor))  # ghi
print(next(genor))  # jkl
for i in genor:
    print(i)
# abc
# def
# ghi
# jkl

 

. 列表推導式,生成器表示式
列表推導式——用一行程式碼構建一個簡單或較複雜的列表
好處:減少程式碼量

li = [i for i in range(1, 101)]
print(li)
# 列表推導式分三種情況:

# 1. 迴圈模式——[變數(加工後的變數) for 變數 in iterable]

# 構建一個列表:['python1期', 'python2期', ...'python25期']
print(["python%i期" % i for i in range(1, 26)])

# 2. 篩選模式——[變數(加工後的變數) for 變數 in iterable if 條件]

# 30以內所有的偶數(兩種方法)
print([i for i in range(2, 31, 2)])
print([i for i in range(1, 31) if i % 2 == 0])

# 其他示例
print([i for i in range(1, 31) if i % 3 == 0])
print([i**2 for i in range(1, 31) if i % 3 == 0])
print(["地球%s號" % i for i in range(1, 100, 2)])

# 3. 三元模式——迴圈模式的變異

# 構建一個列表,裡面的元素是1-20,能被3整除的元素要替換成*
# print("*" if 3 > 2 else 1)
print(["*" if i % 3 == 0 else i for i in range(1, 21)])

# 將至少含有兩個e的名字放到一個列表
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
# 方法一
l = []
for i in names:
    for j in i:
        if j.count("e") >= 2:
            l.append(j)
print(l)

# 方法二
print([j for i in names for j in i if j.count("e") >= 2])

# 列表推導式總結:
# 優點:一行搞定,節省程式碼行數
# 缺點:不能用debug模式,列表推導式相對複雜的列表不能用,三層以上效率低也很難寫

 

# 生成器表示式
# 它與列表推導式寫法幾乎一樣,也有同樣的三種模式,只需將[]換成()就可以

# 列表推導式
print(["python%i期" % i for i in range(1, 26)])

# 生成器表示式
ret = ("python%i期" % i for i in range(1, 26))
for i in ret:
    print(i)

# 元組沒有推導式!!!因為上面的生成器表示式已經用()來表示了

# 字典推導式

print({i:None for i in range(1, 10)})
# {1: None, 2: None, 3: None, 4: None, 5: None, 6: None, 7: None, 8: None, 9: None}

# 將下面字典的鍵值對調
mcase = {"a": 10, "b": 34, "c":20, "d":15}
print({v:k for k,v in mcase.items()})

# 集合推導式

# 將{1, -2, 3, -4, 4}的所有元素取平方
set1 = {1, -2, 3, -4, 4}
print({i**2 for i in set1})
# {16, 1, 4, 9}  順便去重了

 

# 四. 匿名函式lambda

def func(x, y):
    return x + y
print(func(3, 4))

# 像上面這種只有return 即返回值的函式,可以使用匿名函式 lambda
# 匿名函式只能用一行程式碼表示,冒號前面是引數,後面是返回值

func2 = lambda x,y: x + y  # func2就是這個匿名函式的名字
print(func2(3, 4))

# 寫一個匿名函式,需要三個數字引數,返回值為三個數想乘的結果
func3 = lambda a, b, c: a * b * c
print(func3(1, 2, 3))