使用dir()我們可以知道這個數據類型的內置函數有什麼方法: 1.迭代器 iterable:可迭代的 迭代就是將數據能夠一個一個按順序取出來 上面數據類型返回為真說明它是可以迭代的,反之是不可迭代的 可迭代協議: 就是內部要有一個__iter__()來滿足要求 當一個具有可迭代的數據執行__iter ...
使用dir()我們可以知道這個數據類型的內置函數有什麼方法:
print(dir(int)) print(dir(bool)) print(dir([])) print(dir({})) print(dir(set))
1.迭代器
iterable:可迭代的
迭代就是將數據能夠一個一個按順序取出來
s = 'abc' print('__iter__' in dir(s)) #True li = [1,2] print('__iter__' in dir(li)) #True b = False print('__iter__' in dir(b)) #False i = 123 print('__iter__' in dir(i)) #False dic = {} print('__iter__' in dir(dic)) #True set1 = set() print('__iter__' in dir(set1)) #True
上面數據類型返回為真說明它是可以迭代的,反之是不可迭代的
可迭代協議:
就是內部要有一個__iter__()來滿足要求
當一個具有可迭代的數據執行__iter__()它將返回一個迭代器的記憶體地址
print('abc'.__iter__()) #<str_iterator object at 0x005401F0>
這裡的iterator的意思是迭代器
迭代器協議:
現在有一個列表我們來看看它本身和在執行了__iter__()之後的方法有什麼不同:
li = [1,2,3,'a'] print(dir(li)) #['__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'] print(dir(li.__iter__())) #['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__'] '''求兩者的差集''' print(set(dir(li.__iter__())) - set(dir(li))) #{'__length_hint__', '__setstate__', '__next__'}
__length_hint__()的作用是求元素的長度
__setstate__()的作用是指定索引值從哪裡開始迭代
__next__()的作用可以讓值一個一個的取出
在之前用到的for迴圈我們就是用__next__()這種方法進行取值,現在我們可以模擬for來寫一個函數:
li = [1,2,3,'a'] def getItem(li): iterator = li.__iter__() while True: print(iterator.__next__()) getItem(li) # 1 # 2 # 3 # a #StopIteration 如果找不到元素就會報錯
如何處理掉這個異常?
li = [1,2,3,'a'] def getItem(li): iterator = li.__iter__() while True: try: print(iterator.__next__()) except StopIteration: break getItem(li) # 1 # 2 # 3 # a
迭代器遵循迭代器協議:必須要有__iter__()和__next__()
迭代器的好處:
a.從容器類型中一個一個的取值,會把所有的值都取到
b.節省記憶體的空間
迭代器並不會在記憶體中占用一大塊記憶體,而是隨著迴圈每次生成一個,每一次使用__next__()來給值
range():
print(range(10000000)) #range(0, 10000000)
實際上range()在調用的時候並沒有真正生成這麼多的值,如果真的生成的話那麼記憶體可能會溢出
print('__iter__' in dir(range(10))) #True print('__next__' in dir(range(10))) #False from collections import Iterable from collections import Iterator print(isinstance(range(10),Iterable)) #True 是一個可迭代對象 print(isinstance(range(10),Iterator)) #False 在執行後得到的結果並不是一個迭代器
迭代器總結:
1.可以被for迴圈的都是可迭代的
2.可迭代的內部都有__iter__()方法
3.只要是迭代器一定可以迭代
4.可迭代的變數.__iter__()方法可以得到一個迭代器
5.迭代器中的__next__()方法可以一個一個的獲取值
6.for迴圈實際上就是在使用迭代器
2.生成器
生成器函數
本質上就是我們自己寫的函數,只要含有yield關鍵字的函數就是生成器函數,yield不能和return共用且需要寫在函數內部
def generator(): #生成器函數 print('a') yield 5 ret = generator() #ret是一個生成器 print(ret) #<generator object generator at 0x00503F00>
生成器函數每次執行的時候會得到一個生成器作為返回值
如果要返回函數值:
def generator(): #生成器函數 print('a') yield 5 print('b') yield 4 g = generator() #g是一個生成器 print(g) #<generator object generator at 0x00503F00> ret = g.__next__() print(ret) #a #5 print('---------') ret = g.__next__() print(ret) #b #4
執行順序:
使用for迴圈遍歷生成器:
def generator(): #生成器函數 print('a') yield 5 print('b') yield 4 print('c') yield 6 g = generator() #g是一個生成器 for i in g: print(i) # a # 5 # b # 4 # c # 6
監聽文件例子:
def tail(filename): f = open(filename,encoding='utf-8') while True: line = f.readline() if line.strip(): yield line.strip() g = tail('tail') for i in g: if 'python' in i: print('******',i) # ****** python # ****** asd python