[TOC]
## 列表生成式
通過列表生成式,我們可以直接創建一個列表
```
>>> a = [i+1 for i in range(10)]
>>> a
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
```
>通過列表生成式,我們可以直接創建一個列表。但是,列表容量肯定是有限的。如果創建一個包含100萬個元素的列表,不僅占用很大的存儲空間,假如我們僅僅需要訪問前面幾個元素,那后面絕大多數元素占用的空間都白白浪費了。
## 生成器(generator)
通過生成器一邊循環一邊計算的機制,就不必創建完整的list,從而節省大量的空間,如果獨立函數調用,函數可以暫停或者掛起,并在需要時從暫停地方繼續或者重新開始,需要用`yield`實現
### 列表生成器
要創建一個generator,有很多種方法。第一種方法很簡單,只要把一個列表生成式的`[]`改成`()`,就創建了一個generator:
```
>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>
```
創建`L`和`g`的區別僅在于最外層的`[]`和`()`,`L`是一個list,而`g`是一個generator。
#### 獲取生成器的值(next函數)
生成器中的數據,可以通過`next()`函數獲得generator的下一個返回值:
```
>>> next(g)
0
......略
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
```
>generator保存的是算法,每次調用`next(g)`就計算出`g`的下一個元素的值,直到計算到最后一個元素,沒有更多的元素時,拋出`StopIteration`的錯誤。
#### 獲取生成器的值(for循環)
不斷調用`next(g)`實在是太變態了,正確的方法是使用`for`循環,因為generator也是可迭代對象:
```
>>> g = (x * x for x in range(10))
>>> for n in g:
... print(n)
...
0
......略
81
```
>創建了一個generator后,基本上永遠不會調用next(),而是通過for循環來迭代它,并且不需要關心StopIteration的錯誤。
### 函數生成器
generator非常強大。如果推算的算法比較復雜,用類似列表生成式的for循環無法實現的時候,還可以用函數來實現。
如果一個函數定義中包含`yield`關鍵字,那么這個函數就不再是一個普通函數,而是一個generator:
#### 斐波拉契數列生成器
著名的斐波拉契數列(Fibonacci),除第一個和第二個數外,任意一個數都可由前兩個數相加得到:
`1, 1, 2, 3, 5, 8, 13, 21, 34, ...`
```
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
a, b = b, a + b
n = n + 1
return 'done'
```
要把`fib`函數變成generator,只需要把`print(b)`改為`yield b`就可以了:
```
def fib(max):
n,a,b = 0,0,1
while n < max:
#print(b)
yield b
a,b = b,a+b
n += 1
return 'done'
```
驗證一下這個是不是編程函數生成器
```
>>> f = fib(6)
>>> f
<generator object fib at 0x104feaaa0>
```
#### 函數生成器與函數的區別
最難理解的就是generator和函數的執行流程不一樣。函數是順序執行,遇到return語句或者最后一行函數語句就返回。而變成generator的函數,在每次調用next()的時候執行,遇到yield語句返回,再次被next()調用時從上次返回的yield語句處繼續執行。
```
data = fib(10)
print(data)
print(data.__next__())
print(data.__next__())
print("干點別的事")
print(data.__next__())
print(data.__next__())
#>:輸出
<generator object fib at 0x000002E33EEFFCA8>
1
1
干點別的事
2
3
```
> `在上面fib`的例子,我們在循環過程中不斷調用`yield`,就會不斷中斷。當然要給循環設置一個條件來退出循環,不然就會產生一個無限數列出來。同樣的,把函數改成generator后,我們基本上從來不會用`next()`來獲取下一個返回值,而是直接使用`for`循環來迭代:
```
>>> for n in fib(6):
... print(n)
...
1
1
......略
8
```
### 捕獲生成器錯誤(StopIteration)
用for循環調用generator時,發現拿不到generator的return語句的返回值。如果想要拿到返回值,必須捕獲StopIteration錯誤,返回值包含在StopIteration的value中:
```
>>> g = fib(6)
>>> while True:
... try:
... x = next(g)
... print('g:', x)
... except StopIteration as e:
... print('Generator return value:', e.value)
... break
...
g: 1
g: 1
......略
g: 8
Generator return value: done
```
> 關于如何捕獲錯誤的詳細情況,看錯誤捕獲相關筆記。
## 迭代器(Iterator)
迭代器就是可將可迭代對象(如列表、元組、字典等)循環輸出的一種工具,比如`for...in...`就是應用了迭代器的原理。
如果想轉成迭代器可以用`iter()`函數,然后用`next()`函數就可以一個個內容輸出了,或者也可以結合for輸出,每輸出一個,迭代器里就少一個,直到空了就不能輸出了
我們已經知道,可以直接作用于`for`循環的數據類型有以下兩類種:
* 一類是集合數據類型,如`list`、`tuple`、`dict`、`set`、`str`等;
* 一類是`generator`,包括生成器和帶`yield`的generator function。
### 可迭代對象(Iterable)
這些可以直接作用于`for`循環的對象統稱為可迭代對象:`Iterable`。可以使用`isinstance()`判斷一個對象是否是`Iterable`對象:
```
>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False`
```
### 迭代器(Iterator)
而生成器不但可以作用于for循環,還可以被next()函數不斷調用并返回下一個值,直到最后拋出StopIteration錯誤表示無法繼續返回下一個值了。
**可以被next()函數調用并不斷返回下一個值的對象稱為迭代器:Iterator。**
可以使用isinstance()判斷一個對象是否是Iterator對象:
```
>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False
```
生成器都是`Iterator`對象,但`list`、`dict`、`str`雖然是`Iterable`,卻不是`Iterator`。
### 可迭代對象轉為迭代器
把`list`、`dict`、`str`等`Iterable`變成`Iterator`可以使用`iter()`函數:
```
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True
```
>為什么`list`、`dict`、`str`等數據類型不是`Iterator`?
>>這是因為Python的`Iterator`對象表示的是一個數據流,Iterator對象可以被`next()`函數調用并不斷返回下一個數據,直到沒有數據時拋出`StopIteration`錯誤。可以把這個數據流看做是一個有序序列,但我們卻不能提前知道序列的長度,只能不斷通過`next()`函數實現按需計算下一個數據,所以`Iterator`的計算是惰性的,只有在需要返回下一個數據時它才會計算。
`Iterator`甚至可以表示一個無限大的數據流,例如全體自然數。而使用list是永遠不可能存儲全體自然數的。
## 迭代器概念總結
* 凡是可作用于`for`循環的對象都是`Iterable`類型;
* 凡是可作用于`next()`函數的對象都是`Iterator`類型,它們表示一個惰性計算的序列;
* 集合數據類型如`list`、`dict`、`str`等是`Iterable`但不是`Iterator`,不過可以通過`iter()`函數獲得一個`Iterator`對象。
- 基礎部分
- 基礎知識
- 變量
- 數據類型
- 數字與布爾詳解
- 列表詳解list
- 字符串詳解str
- 元組詳解tup
- 字典詳解dict
- 集合詳解set
- 運算符
- 流程控制與循環
- 字符編碼
- 編的小程序
- 三級菜單
- 斐波那契數列
- 漢諾塔
- 文件操作
- 函數相關
- 函數基礎知識
- 函數進階知識
- lambda與map-filter-reduce
- 裝飾器知識
- 生成器和迭代器
- 琢磨的小技巧
- 通過operator函數將字符串轉換回運算符
- 目錄規范
- 異常處理
- 常用模塊
- 模塊和包相關概念
- 絕對導入&相對導入
- pip使用第三方源
- time&datetime模塊
- random隨機數模塊
- os 系統交互模塊
- sys系統模塊
- shutil復制&打包模塊
- json&pickle&shelve模塊
- xml序列化模塊
- configparser配置模塊
- hashlib哈希模塊
- subprocess命令模塊
- 日志logging模塊基礎
- 日志logging模塊進階
- 日志重復輸出問題
- re正則表達式模塊
- struct字節處理模塊
- abc抽象類與多態模塊
- requests與urllib網絡訪問模塊
- 參數控制模塊1-optparse-過時
- 參數控制模塊2-argparse
- pymysql數據庫模塊
- requests網絡請求模塊
- 面向對象
- 面向對象相關概念
- 類與對象基礎操作
- 繼承-派生和組合
- 抽象類與接口
- 多態與鴨子類型
- 封裝-隱藏與擴展性
- 綁定方法與非綁定方法
- 反射-字符串映射屬性
- 類相關內置方法
- 元類自定義及單例模式
- 面向對象的軟件開發
- 網絡-并發編程
- 網絡編程SOCKET
- socket簡介和入門
- socket代碼實例
- 粘包及粘包解決辦法
- 基于UDP協議的socket
- 文件傳輸程序實戰
- socketserver并發模塊
- 多進程multiprocessing模塊
- 進程理論知識
- 多進程與守護進程
- 鎖-信號量-事件
- 隊列與生產消費模型
- 進程池Pool
- 多線程threading模塊
- 進程理論和GIL鎖
- 死鎖與遞歸鎖
- 多線程與守護線程
- 定時器-條件-隊列
- 線程池與進程池(新方法)
- 協程與IO模型
- 協程理論知識
- gevent與greenlet模塊
- 5種網絡IO模型
- 非阻塞與多路復用IO實現
- 帶著目標學python
- Pycharm基本使用
- 爬蟲
- 案例-爬mzitu美女
- 案例-爬小說
- beautifulsoup解析模塊
- etree中的xpath解析模塊
- 反爬對抗-普通驗證碼
- 反爬對抗-session登錄
- 反爬對抗-代理池
- 爬蟲技巧-線程池
- 爬蟲對抗-圖片懶加載
- selenium瀏覽器模擬