可迭代對象
我們現在是從結果分析原因,能被for循環的就是“可迭代的”,但是如果正著想,for怎么知道誰是可迭代的呢?
假如我們自己寫了一個數據類型,希望這個數據類型里的東西也可以使用for被一個一個的取出來,那我們就必須滿足for的要求。這個要求就叫做“協議”。
可以被迭代要滿足的要求就叫做可迭代協議。可迭代協議的定義非常簡單,就是內部實現了__iter__()
方法。
如果這個對象中有_ iter _()
方法,這個對象就是可迭代對象
if '__iter__' in dir(str)
通俗易懂 :可以被for循環迭代的對象就是可迭代對象。
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
迭代器
迭代器是什么
迭代器只不過是一個實現迭代器協議的容器對象。它基于兩個方法:
next 返回容器的下一個項目
_ iter _ 返回迭代器本身
i = iter('asd')
next(i)
>>>'a'
next(i)
>>>'s'
next(i)
>>>'d'
next(i)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
當序列便利完時,將拋出一個StopIteration異常。這將使迭代器與循環兼容,因為它們將捕獲這個異常以停止循環。要創建定制的迭代器,可以編寫一個具有next
方法的類
為什么使用迭代器
“流式”數據處理方式*少內存消耗
例如: 當處理文件時,把所有數據全部取出來放到內存里面進行處理會導致程序消耗大量內存
一般我們會一部分一部分的對文件內容進行處理:
for text_line in open("xx.txt"):
print text_line
open("xx.txt")
返回的是可迭代的對象,所以,可以漸進式地對文件的內容進行處理,即按行來讀取文件,并進行處理,
而不是,直接把全部文件一下加載到內存中。
支持方便用for語句對數據進行處理
python內置的一些常見的像類型像數組、列表甚至字符串等都是可迭代類型,
這樣我們就能方便for語句這個語法方便對數據進行消費,不需要自己記錄索引位置,人肉循環:
for i in [1,2,3,4]:
print(i)
迭代器的優點
很方便使用,且只能取所有的數據取一次
節省內存空間
迭代器的內部
可迭代對象:
對象里面包含__iter()__
方法的實現,對象的iter
函數經調用之后會返回一個迭代器,里面包含具體數據獲取的實現
迭代器:
包含有next
方法的實現,在正確范圍內返回期待的數據以及超出范圍后能夠拋出StopIteration
的錯誤停止迭代。
迭代器不但可以作用于for
循環,還可以被next()
函數不斷調用并返回下一個值,直到最后拋出StopIteration
錯誤表示無法繼續返回下一個值了。
可以被next()
函數調用并不斷返回下一個值的對象稱為迭代器:Iterator
。
生成器都是Iterator
對象,但list、dict、str
雖然是Iterable
,卻不是Iterator
把list、dict、str
等Iterable
變成Iterator
可以使用iter()
函數:
迭代協議
必須擁有_iter_
方法和_next_
方法。
迭代器和可迭代對象
正式的說法:
一個實現了iter
方法的對象是可迭代的,一個實現next
方法的對象則是迭代器
個人理解:
僅實現了iter
方法的是可迭代對象,可迭代對象可以通過._ iter _()
方法轉成迭代器。
迭代器有next
和_ iter _
方法 只要存在這兩個方法的都是迭代器。迭代器是一個存儲的容器,每當調用next
方法的時候才會將數據拿出。并且只執行一次
即迭代器可以作為一個數據生成器
生成器
生成器的本質就是迭代器
迭代器 我們知道是用來迭代可迭代對象的,而生成器是用來迭代方法的
我們知道的迭代器有兩種:一種是調用方法直接返回的,一種是可迭代對象通過執行iter方法得到的,迭代器有的好處是可以節省內存。
如果在某些情況下,我們也需要節省內存,就只能自己寫。我們自己寫的這個能實現迭代器功能的東西就叫生成器。
生成器Generator:
本質:迭代器(所以自帶了iter方法和next方法,不需要我們去實現)
特點:惰性運算,開發者自定義
1.生成器函數:
常規函數定義,但是,使用yield
語句而不是return
語句返回結果。yield
語句一次返回一個結果,在每個結果中間,掛起函數的狀態,以便下次重它離開的地方繼續執行
只要有 yield
的關鍵字的函數就是生成器函數
一個包含yield
關鍵字的函數就是一個生成器函數。yield
可以為我們從函數中返回值,但是yield
又不同于return
,return
的執行意味著程序的結束,調用生成器函數不會得到返回的具體的值,而是得到一個可迭代的對象。每一次獲取這個可迭代對象的值,就能推動函數的執行,獲取新的返回值。直到函數執行結束。
- 特點:
調用函數的之后函數不執行,返回一個生成器
每次調用next方法的時候會取到一個值
直到取完最后一個,在執行next會報錯
從生成器中取值的幾個方法
1.next
2.for
3.數據類型的強制轉換 : 占用內存
例1:
監聽文件
import time
def tail(filename):
f = open(filename)
while True:
line = f.readline() # 讀取文件中新的文本行
if not line:
time.sleep(0.1)
break
yield line
調用
tail_g = tail('tmp')
for line in tail_g:
print(line)
例2
預激協程的裝飾器
計算移動平均值
def init(func):
def inner(*args,**kwargs):
g = func(*args,**kwargs)
next(g)
return g
return inner
@init
def averager():
total = 0.0
count = 0
average = None
while 1:
trem = yield average
total += term
count += 1
average = total / count
調用
g_avg = averager()
print(g_avg.send(10))
print(g_avg.send(30))
print(g_avg.send(5))
理解
我們都知道 生成器函數只有當調用next方法時,才會進入函數內部進行執行,為了方便調用 將next放到裝飾器中,這樣使調用更方便、易懂
當然我們就上述代碼也可以不寫裝飾器 而寫成
g_avg = averager()
next(g_avg)
print(g_avg.send(10))
print(g_avg.send(30))
print(g_avg.send(5))
但是試想,如果有N多生成器函數,我們需要N多next()方法,語句看起來也會混亂
yield from
def gen1():
for c in 'AB':
yield c
for i in range(3):
yield i
print(list(gen1()))
def gen2():
yield from 'AB'
yield from range(3)
print(list(gen2()))
2.生成器表達式:
類似于列表推導,但是,生成器返回按需產生結果的一個對象,而不是一次構建一個結果列表
語法:
(x for x in range(10))
這和列表生成式很類似,區別在于一個是()
一個是[]
,語法是一樣的
列表生成式
生成的結果是一個列表
語法:
[x for x in range(10)]
從數組中依次將內容取出
相當于
x = []
for i in range(10):
x.append(i)
[x * 2 for x in range(10) if x % 2 == 0]
從數組中 將 符合條件 的內容取出, 并計算
相當于
x = []
for i in range(10):
if i % 2 ==0:
x.append(i * 2)
[x * y for x in range(10) for y in range(10)]
循環嵌套
相當于
x = []
for x in range(10):
for y in range(10):
x.append(x * y)
[x % 2 == 0 for x in range(10)]
for x in range(10):
if x % 2 == 0:
return True
else:
return False