迭代
每一次
對過程的重複
稱爲一次“迭代”,而每一次迭代得到的結果
會作爲下一次迭代的初始值
。例如:循環獲取容器中的元素。
可迭代對象iterable
具有__iter__函數的對象,可以返回迭代器對象。
對list、tuple、dict、set、str等類型的數據可以通過for…in…這類語句迭代讀取一條數據供我們使用的對象稱之爲可迭代對象(Iterable)。
class 可迭代對象名稱:
def __iter__(self):
return 迭代器
簡單總結:
迭代器 = 可迭代對象.iter()
可以被next()函數調用並返回下一個值的對象
迭代器對象iterator
可以被next()函數調用並返回下一個值的對象。
實現了__next__()魔法方法(類實例化),該方法返回迭代器下一個值(保存得到下一個迭代值的算法)
class 迭代器類名:
def __init__(self, 聚合對象):
self.聚合對象= 聚合對象
def __next__(self):
if 沒有元素:
raise StopIteration
return 聚合對象元素
理解可迭代對象iterable 與迭代器對象iterator
可迭代對象包含迭代器。
如果一個對象擁有__iter__方法,其是可迭代對象;如果一個對象擁有__next__方法,其是迭代器。
定義可迭代對象,必須實現__iter__方法;定義迭代器,必須實現__iter__和__next__方法。
使用__iter__與__next__實現迭代(不使用for):
# 定義list類型的可迭代對象
my_list = [1, 2, 3, 7, 8, 9]
# 獲取迭代器
# iterator = my_list.__iter__() # 直接寫法
# iter()函數實際上就是調用了可迭代對象的__iter__方法
iterator = iter(my_list)
# 循環獲取下一個元素
while True:
try:
# item = iterator.__next__() 直接寫法
item = next(iterator) # 與iter() 同理
print(item)
# 異常處理
except StopIteration: # 迭代完成
break
使用函數定義__iter__與__next__實現迭代:
class MyIterator:
def __init__(self, target):
self.target = target
# 下標計數器
self.index = 0
def __next__(self):
# 如果 下標計數器 > 可迭代對象長度
if self.index > len(self.target) - 1:
# 報錯
raise StopIteration()
result = self.target[self.index]
self.index += 1
return result
class MyManager:
"""
可迭代對象
"""
def __init__(self):
# 定義一個函數私有變量__list 類型爲list
self.__list = []
def add_obj(self, obj):
self.__list.append(obj)
def __iter__(self):
# 返回MyIterator類 實例化對象
return MyIterator(self.__list)
class Test:
def __str__(self):
return "這是Test類"
# 實例化MyManager類
manager = MyManager()
manager.add_obj(Test())
manager.add_obj(Test())
manager.add_obj(Test())
iterator = manager.__iter__()
while True:
try:
item = iterator.__next__()
print(item)
except StopIteration:
break
生成器generator
能夠動態(循環一次計算一次返回一次)提供數據的可迭代對象。
作用:在循環過程中,按照某種算法推算數據,不必創建容器存儲完整的結果,從而節省內存空間。數據量越大,優勢越明顯。在Python中,這種一邊循環一邊計算的機制,稱爲生成器:generator
ps: 當數據量足夠大時,內存是完全不夠用的,但是又需要迭代計算時,需要惰性操作 : 執行一次,計算一次,而不是創建容器,來存儲整個對象
生成器函數
含有yield語句的函數,返回值爲生成器對象。
yield翻譯爲”產生”或”生成”
調用生成器函數將返回一個生成器對象,不執行函數體。
# 創建生成器函數
def 函數名A():
…
yield 數據
# 使用生成器函數
for i in 函數名A():
語句
舉例來說:
def Test():
print('Test1')
yield 'Test - Test1'
print('Test2')
yield 'Test - Test2'
print('Test3')
yield 'Test - Test3'
t = Test()
result = next(t)
print(result)
result = next(t)
print(result)
result = next(t)
print(result)
result = next(t) # 第四次調用 超出迭代範圍
print(result)
Test1
Test - Test1
Test2
Test - Test2
Test3
Test - Test3
Traceback (most recent call last):
File "my_test_2.py", line 19, in <module>
result = next(t)
StopIteration
執行過程:
Setp1 - 調用生成器函數會自動創建迭代器對象。
Setp2 - 調用迭代器對象的__next__()方法時才執行生成器函數。
Setp3 - 每次執行到yield語句時返回數據,暫時離開。
Setp4 - 待下次調用__next__()方法時繼續從離開處繼續執行。
優點: 不會將所有結果計算出來,存儲在內存中
缺點: 無法通過索引,切片靈活的訪問結果
- 將
延遲操作轉爲立即操作 list() 函數
從而克服缺點
注意:
生成器只可for遍歷一次,第二次遍歷無效,需要重新調用生成器函數產生生成器
def test(l : list):
for i in l:
yield i
if __name__ == '__main__':
result = test([1, 2, 3])
for i in result:
print(i)
for i in result:
print(i)
# 結果 只調用一次:
1
2
3
使用 yeild 改寫MyManager 實現惰性操作
# class MyIterator: 全部註釋掉
#
# def __init__(self, target):
# self.target = target
# # 下標計數器
# self.index = 0
#
# def __next__(self):
# # 如果 下標計數器 > 可迭代對象長度
# if self.index > len(self.target) - 1:
# # 報錯
# raise StopIteration()
#
# result = self.target[self.index]
# self.index += 1
# return result
class MyManager:
"""
可迭代對象
"""
def __init__(self):
# 定義一個函數私有變量__list 類型爲list
self.__list = []
def add_obj(self, obj):
self.__list.append(obj)
def __iter__(self):
# return MyIterator(self.__list)
for item in self.__list:
# 將函數變爲 生成器函數
yield item
class Test:
def __str__(self):
return "這是Test類"
# 實例化MyManager類
manager = MyManager()
manager.add_obj(Test())
manager.add_obj(Test())
manager.add_obj(Test())
iterator = manager.__iter__()
while True:
try:
item = iterator.__next__()
print(item)
except StopIteration:
break
lambda表達式
通常是在需要一個函數,但是又不想費神去命名一個函數的場合下使用,也就是指匿名函數。
作用:作爲參數傳遞時語法簡潔,優雅,代碼可讀性強。
定義方法:
變量 = lambda 形參: 方法體
– 形參沒有可以不填
– 方法體只能有一條語句,且不支持賦值語句。
# 普通函數
def condition1(item):
return item // 2
# lambda 匿名函數
num = lambda target: target // 2
if __name__ == '__main__':
print(condition1(100)) # 50
print(num(100)) # 50
相對普通方法的優缺點:
優點:
省略方法名
減少程序間依賴性
將方法作爲參數,特別優雅
缺點:
由於沒有名字,代碼不能複用
方法體只能一條語句 錯誤方法: a = lambda item:print(“ok”) ; item % 2 == 0
方法體必須有返回值
Python之禪中有這麼一句話:Explicit is better than implicit(明瞭勝於晦澀),就是說那種方式更清晰就用哪一種方式,不要盲目的都使用lambda表達式。