Python可迭代對象、迭代器和生成器
- 2020 年 7 月 11 日
- 筆記
- Python高級編程
Python可迭代對象、迭代器和生成器
總覽:可迭代對象、迭代器、生成器之間的關係
- 可迭代對象:可以進行for循環的都是可迭代對象,原因是其內部實現了一個
__iter__
方法 - 迭代器:能夠用next()函數,都是迭代器對象,其內部實現了
__iter__
和__next__
方法 - 生成器:元組推導式和函數里使用yield的函數都是生成器
- 生成器是一種特殊的迭代器,迭代器也是可迭代對象,可迭代對象可通過iter()函數轉化為成為迭代器
- 容器(列表,元組,字典,集合)是可迭代對象,可迭代對象調用 iter() 函數,可以得到一個迭代器。迭代器可以通過 next() 函數來得到下一個元素,從而支援遍歷。
1.可迭代對象和迭代器
1.1 基礎概念
- 所有的可迭代對象均內置了
_iter_()
方法,調用iter()方法,返回值就是一個迭代器 - 迭代器中內置了
_next_()
方法,調用該方法,會返回迭代器對象的每個元素,因此迭代就是從迭代器中取元素的過程 - python中的列表、字典、元組、字元串都是可迭代對象,可迭代對象都可以用for循環實現迭代遍歷。
1.2 判斷
from collections.abc import Iterable, Iterator
a = [1, 2, 3]
b = iter(a) # 可迭代對象調用內置iter()方法返回一個迭代器
isinstance(a, Iterable)
isinstance(b, Iterator)
isinstance(b, Iterable)
1.3 for循環本質
調用可迭代對象的
_iter_()
方法,得到該對象對應的迭代器對象,然後無限調用_next_()
方法,得到對象中的每一個元素,直到Stopiteration異常,代表迭代器中已無下一個元素,for循環自動處理該異常,跳出循環。
# 字典的鍵,值,鍵值對都是可迭代對象
for key in {'one':1, 'two':2}:
print(key)
# 字元串是可迭代對象
for char in "123":
print(char)
# 打開的text同樣是可迭代對象
for line in open("myfile.txt"):
print(line, end='')
1.4 不想用for循環迭代了,如何使用迭代器?
- 先調用容器(以字元串為例)的iter()函數
- 再使用 next() 內置函數來調用
__next__()
方法 - 當元素用盡時,
__next__()
將引發 StopIteration 異常
1.5 列表推導式
- 用 []
li = [i for i in range(10)]
print(li)
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 每個元素在生成後都會存在記憶體中,如果元素很多,就會佔用很大的存儲空間
迭代器實現一個列表:[i for i in range(1000)]
,
2. 生成器Generator
2.1 概念
在Python中,我們把一邊循環一邊計算的機制,稱為生成器。生成器也是一種迭代器,但由於它們並沒有把所有的值存在記憶體中,而是在運行時生成值,因此只能迭代一次。
使用生成器,可以寫出來更加清晰的程式碼;合理使用生成器,可以降低記憶體佔用、優化程式結構、提高程式速度。
2.2 如何實現和使用?
2.2.1 生成器表達式(元組推導式)
- 用()
ge = (i for i in range(10))
print(li)
# <generator object <genexpr> at 0x7f4f446a21d0>
next(ge)
# 0
- 生成器表達式可以認為是一種特殊的生成器函數,返回生成器對象,一次只返回一個值
2.2.2 帶有關鍵字yield的函數
- 程式運行到yied這一行的時候,生成器調用next()函數生成一個值,同時暫停程式,直到下次調用next()函數時才激活,從上次離開的位置恢復執行
def reverse(data):
for index in range(len(data)):
yield data[index]
print("大大")
# reverse('golf'), 此條語句返回一個生成器對象(也是可迭代對象),for循環實現遍歷沒毛病
for char in reverse('golf'):
print(char)
# 輸出
g
o
l
f
大大
# 遍歷方法2
char = reverse('golf') # 返回一個生成器對象,<generator object reverse at 0x7f71c8124250>
print(next(char))
print(next(char))
print(next(char))
print(next(char))
print(next(char))
# 輸出
g
o
l
f
大大
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
3. 應用舉例
3.1 給定一個list和一個指定數字,求這個數字在list中的位置
# 常規for循環遍歷
def index_normal(L, target):
result = []
for i, num in enumerate(L):
if num == target:
result.append(i)
return result
print(index_normal([1, 6, 2, 4, 5, 2, 8, 6, 3, 2], 2))
# 使用生成器
def index_generator(L, target):
for i, num in enumerate(L):
if num == target:
yield i
# index_generator會返回一個生成器對象,需要使用list轉換為列表後,才能print輸出
print(list(index_generator([1, 6, 2, 4, 5, 2, 8, 6, 3, 2], 2)))
# 輸出
[2, 5, 9]
3.2 給定兩個序列,判定第一個是不是第二個的子序列
解析:序列就是列表,子序列指的是一個列表的元素在第二個列表中都按順序出現,但是並不必挨在一起
def is_subsequence(a, b):
b = iter(b) # 把列表b轉化成一個迭代器
return all(i in b for i in a)
# (i for i in a),將列表a初始化為一個生成器,可以遍歷對象a
# i in b,判斷生成器next()函數遍歷a的指是否在迭代器b調用next()得到的對象中
# all函數,判斷一個迭代器的元素是否全部為True
print(is_subsequence([1, 3, 5], [1, 2, 3, 4, 5]))
print(is_subsequence([1, 4, 3], [1, 2, 3, 4, 5]))
# 輸出
True
False
3.3 計算0-9數字的平方和
sum(i*i for i in range(10))
# 285
3.4 web自動化測試pytest框架,測試夾具設置前後置條件
@pytest.fixture(scope="class")
def browser():
"""啟動和關閉瀏覽器"""
# 初始化瀏覽器
driver = webdriver.Chrome()
# 設置隱式等待
driver.implicitly_wait(10)
# 瀏覽器頁面最大化
driver.maximize_window()
# 返回一個瀏覽器對象
yield driver
driver.quit()
參考文章:
[1]//zhuanlan.zhihu.com/p/76831058
[2]//blog.csdn.net/baidu_28289725/article/details/80622454