1. 程式人生 > >Python可迭代物件、迭代器和生成器

Python可迭代物件、迭代器和生成器

## Python可迭代物件、迭代器和生成器 [TOC] #### 總覽:可迭代物件、迭代器、生成器之間的關係 - **可迭代物件:**可以進行for迴圈的都是可迭代物件,原因是其內部實現了一個`__iter__`方法 - **迭代器**:能夠用next()函式,都是迭代器物件,其內部實現了`__iter__`和`__next__`方法 - **生成器**:元組推導式和函式裡使用yield的函式都是生成器 - **生成器是一種特殊的迭代器,迭代器也是可迭代物件,可迭代物件可通過iter()函式轉化為成為迭代器** - 容器(列表,元組,字典,集合)是可迭代物件,可迭代物件呼叫 iter() 函式,可以得到一個迭代器。迭代器可以通過 next() 函式來得到下一個元素,從而支援遍歷。 #### 1.可迭代物件和迭代器 ##### 1.1 基礎概念
- 所有的可迭代物件均內建了`_iter_()`方法,呼叫iter()方法,返回值就是一個迭代器 - 迭代器中內建了`_next_()`方法,呼叫該方法,會返回迭代器物件的每個元素,因此迭代就是從迭代器中取元素的過程 - python中的列表、字典、元組、字串都是可迭代物件,可迭代物件都可以用for迴圈實現迭代遍歷。 ##### 1.2 判斷 ```python 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迴圈自動處理該異常,跳出迴圈。 ```python # 字典的鍵,值,鍵值對都是可迭代物件 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迴圈迭代了,如何使用迭代器? 1. 先呼叫容器(以字串為例)的iter()函式 2. 再使用 *next()* 內建函式來呼叫 *`__next__()`* 方法 3. 當元素用盡時,`__next__()` 將引發 StopIteration 異常
##### 1.5 列表推導式 - 用 **[]** ```python 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 生成器表示式**(元組推導式) - 用**()** ```python ge = (i for i in range(10)) print(li) # at 0x7f4f446a21d0> next(ge) # 0 ``` - 生成器表示式可以認為是一種特殊的生成器函式,返回生成器物件,一次只返回一個值 ###### **2.2.2 帶有關鍵字yield的函式** - 程式執行到yied這一行的時候,生成器呼叫next()函式生成一個值,同時暫停程式,直到下次呼叫next()函式時才啟用,從上次離開的位置恢復執行 ```python 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') # 返回一個生成器物件, 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中的位置 ```python # 常規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 給定兩個序列,判定第一個是不是第二個的子序列 解析:序列就是列表,子序列指的是一個列表的元素在第二個列表中都按順序出現,但是並不必挨在一起 ```python 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數字的平方和 ```python sum(i*i for i in range(10)) # 285 ``` ##### 3.4 web自動化測試pytest框架,測試夾具設定前後置條件 ```python @pytest.fixture(scope="class") def browser(): """啟動和關閉瀏覽器""" # 初始化瀏覽器 driver = webdriver.Chrome() # 設定隱式等待 driver.implicitly_wait(10) # 瀏覽器頁面最大化 driver.maximize_window() # 返回一個瀏覽器物件 yield driver driver.quit() ``` 參考文章: [1]https://zhuanlan.zhihu.com/p/76831058 [2]https://blog.csdn.net/baidu_28289725/article/details/80622454 [3]https://time.geekbang.org/column/article/101521?utm_source=pinpaizhuanqu&utm_medium=geektime&utm_campaign=guanwang&utm_term=guanwang&utm_content=0511