字符串、列表、元組、字典、集合均可以被for循環,說明他們都是可迭代的。html
咱們怎麼來證實這一點呢?編程
from collections import Iterable l = [1,2,3,4] t = (1,2,3,4) d = {1:2,3:4} s = {1,2,3,4} print(isinstance(l,Iterable)) print(isinstance(t,Iterable)) print(isinstance(d,Iterable)) print(isinstance(s,Iterable))
咱們如今是從結果分析緣由,能被for循環的就是「可迭代的」,可是若是正着想,for怎麼知道誰是可迭代的呢?app
假如咱們本身寫了一個數據類型,但願這個數據類型裏的東西也可使用for被一個一個的取出來,那咱們就必須知足for的要求。這個要求就叫作「協議」。ssh
能夠被迭代要知足的要求就叫作可迭代協議。可迭代協議的定義很是簡單,就是內部實現了__iter__方法。函數
接下來咱們就來驗證一下:spa
print(dir([1,2]))
print(dir((2,3)))
print(dir({1:2}))
print(dir({1,2}))code
結果:
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'count', 'index']
['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']
['__and__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__iand__', '__init__', '__ior__', '__isub__', '__iter__', '__ixor__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__or__', '__rand__', '__reduce__', '__reduce_ex__', '__repr__', '__ror__', '__rsub__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__xor__', 'add', 'clear', 'copy', 'difference', 'difference_update', 'discard', 'intersection', 'intersection_update', 'isdisjoint', 'issubset', 'issuperset', 'pop', 'remove', 'symmetric_difference', 'symmetric_difference_update', 'union', 'update']orm
驗證結果htm
總結一下咱們如今所知道的:能夠被for循環的都是可迭代的,要想可迭代,內部必須有一個__iter__方法。對象
接着分析,__iter__方法作了什麼事情呢?
可迭代的:內部必須含有一個__iter__方法。
什麼叫作迭代器?迭代器英文意思是iterator。
l = [1,2,3,4] l_iter = l.__iter__() # 將可迭代的轉化成迭代器 item = l_iter.__next__() print(item) item = l_iter.__next__() print(item) item = l_iter.__next__() print(item) item = l_iter.__next__() print(item) item = l_iter.__next__() print(item)
迭代器遵循迭代器協議:必須擁有__iter__方法和__next__方法。
for循環,能遍歷一個可迭代對象,他的內部到底進行了什麼?
用while循環模擬for循環:
l = [1,2,3,4] l_iter = l.__iter__() while True: try: item = l_iter.__next__() print(item) except StopIteration: break
基於上面講的列表這一大堆遍歷方式,聰明的你立馬看除了端倪,因而你不知死活大聲喊道,你這不逗我玩呢麼,有了下標的訪問方式,我能夠這樣遍歷一個列表啊
=[1,2,3] index=0 while index < len(l): print(l[index]) index+=1 #要毛線for循環,要毛線可迭代,要毛線迭代器
沒錯,序列類型字符串,列表,元組都有下標,你用上述的方式訪問,perfect!可是你可曾想過非序列類型像字典,集合,文件對象的感覺,因此嘛,年輕人,for循環就是基於迭代器協議提供了一個統一的能夠遍歷全部對象的方法,即在遍歷以前,先調用對象的__iter__方法將其轉換成一個迭代器,而後使用迭代器協議去實現循環訪問,這樣全部的對象就均可以經過for循環來遍歷了,並且你看到的效果也確實如此,這就是無所不能的for循環,最重要的一點,轉化成迭代器,在循環時,同一時刻在內存中只出現一條數據,極大限度的節省了內存~
咱們知道的迭代器有兩種:一種是調用方法直接返回的,一種是可迭代對象經過執行iter方法獲得的,迭代器有的好處是能夠節省內存。
若是在某些狀況下,咱們也須要節省內存,就只能本身寫。咱們本身寫的這個能實現迭代器功能的東西就叫生成器。
Python中提供的生成器:
1.生成器函數:常規函數定義,可是,使用yield語句而不是return語句返回結果。yield語句一次返回一個結果,在每一個結果中間,掛起函數的狀態,以便下次重它離開的地方繼續執行
2.生成器表達式:相似於列表推導,可是,生成器返回按需產生結果的一個對象,而不是一次構建一個結果列表
生成器Generator:
本質:迭代器(因此自帶了__iter__方法和__next__方法,不須要咱們去實現)
特色:惰性運算,開發者自定義
一個包含yield關鍵字的函數就是一個生成器函數。yield能夠爲咱們從函數中返回值,可是yield又不一樣於return,return的執行意味着程序的結束,調用生成器函數不會獲得返回的具體的值,而是獲得一個可迭代的對象。每一次獲取這個可迭代對象的值,就能推進函數的執行,獲取新的返回值。直到函數執行結束。
import time
def genrator_fun1():
a = 1
print('如今定義了a變量')
yield a
b = 2
print('如今又定義了b變量')
yield b
g1 = genrator_fun1()
print('g1 : ',g1) #打印g1能夠發現g1就是一個生成器
print('-'*20) #我是華麗的分割線
print(next(g1))
time.sleep(1) #sleep一秒看清執行過程
print(next(g1))
初識生成器函數
初始生成器函數
生成器有什麼好處呢?就是不會一會兒在內存中生成太多數據
假如我想讓工廠給學生作校服,生產2000000件衣服,我和工廠一說,工廠應該是先答應下來,而後再去生產,我能夠一件一件的要,也能夠根據學生一批一批的找工廠拿。
而不能是一說要生產2000000件衣服,工廠就先去作生產2000000件衣服,等回來作好了,學生都畢業了。。。
def produce():
"""生產衣服"""
for i in range(2000000):
yield "生產了第%s件衣服"%i
product_g = produce()
print(product_g.__next__()) #要一件衣服
print(product_g.__next__()) #再要一件衣服
print(product_g.__next__()) #再要一件衣服
num = 0
for i in product_g: #要一批衣服,好比5件
print(i)
num +=1
if num == 5:
break
def generator(): print(123) content = yield 1 print('=======',content) print(456) yield2 g = generator() ret = g.__next__() print('***',ret) ret = g.send('hello') #send的效果和next同樣 print('***',ret) #send 獲取下一個值的效果和next基本一致 #只是在獲取下一個值的時候,給上一yield的位置傳遞一個數據 #使用send的注意事項 # 第一次使用生成器的時候 是用next獲取下一個值 # 最後一個yield不能接受外部的值
l = [i for i in range(10)] print(l) l1 = ['選項%s'%i for i in range(10)] print(l1)
1.把列表解析的[]換成()獲得的就是生成器表達式
2.列表解析與生成器表達式都是一種便利的編程方式,只不過生成器表達式更節省內存
3.Python不但使用迭代器協議,讓for循環變得更加通用。大部份內置函數,也是使用迭代器協議訪問對象的。例如, sum函數是Python的內置函數,該函數使用迭代器協議訪問對象,而生成器實現了迭代器協議,因此,咱們能夠直接這樣計算一系列值的和:
sum(x ** 2 for x in range(4))
更多精彩請見——迭代器生成器專題:http://www.cnblogs.com/jin-xin/articles/8423802.html