迭代器:迭代器協議和可迭代協議,for 迴圈的運行邏輯,迭代器的好處 ...
迭代器
首先我們查看下列類型擁有的所有方法(會顯示很多)
print(dir([])) print(dir({})) print(dir('')) print(dir(range(10)))
#求下上面列表中方法的交集 ret = set(dir([]))&set(dir({}))&set(dir(''))&set(dir(range(10))) #set 轉化為列表 print(ret) #可以註意到都有這兩個雙下方法 # __init__ __iter__ #我們來舉一個雙下方法的例子 #舉例:列表的相加 print([1].__add__([2])) print([1]+[2]) #實際執行的是 print([1].__add__([2])) #接著我來看一下 int 類型 for i in 123: print(i) #不可被迴圈的會提示該報錯:TypeError: 'int' object is not iterable(iterable:可迭代的) #原因是 int 類型沒有該方法 __iter__
判斷數據類型中是否有 __iter__ 方法
print('__iter__' in dir(int)) #False print('__iter__' in dir(bool)) #False print('__iter__' in dir(list)) print('__iter__' in dir(dict)) print('__iter__' in dir(set)) print('__iter__' in dir(tuple)) print('__iter__' in dir(enumerate([]))) print('__iter__' in dir(range(1))) #方法之間也可以相減 去重(主要是查看 並沒有什麼用) print(set(dir([].__iter__())) - set(dir([])))
只要是能被for迴圈的數據類型 就一定擁有__iter__方法,一個列表執行了__iter__()之後的返回值就是一個迭代器
print([].__iter__()) <list_iterator object at 0x0000000000A8F2E8> #iterator 迭代器,返回的是一個記憶體地址
#使用 __next__ 一個一個的取值 l = [1,2,3] iterator = l.__iter__() print(iterator) #列表是可以迭代的,當可迭代的後面加上 .__iter__() 方法,就可以得到一個迭代器,返回是一個記憶體地址 print(iterator.__next__()) print(iterator.__next__()) print(iterator.__next__()) #當無值可取時會拋出 StopIteration 的異常 print(iterator.__next__()) #for 迴圈就是一個迭代器 當無值可取時也會出現該異常,但是 for 已經幫我們處理了 #__length_hint__ 計算元素個數 print([1,'a','bbb'].__iter__().__length_hint__())
Iterable 可迭代的 ——> __iter__ 只要含有 __iter__ 方法的都是可迭代的 —— 可迭代協議
[].__iter__() 迭代器 -- > __next__ 通過 next 就可以從迭代器中一個一個的取值
簡單的證明一下上面的結論
from collections import Iterable from collections import Iterator class A: def __iter__(self):pass #在這兩處添加註釋 然後查看下麵的輸出結果 def __next__(self):pass #兩次查看做下對比 a = A() print(isinstance(a,Iterator)) print(isinstance(a,Iterable))
迭代器的概念
迭代器協議 —— 內部含有 __next__ 和 __iter__ 方法的就是迭代器
迭代器協議和可迭代協議
要含有 __iter__ 方法的都是可迭代的 —— 可迭代協議 部含有 __next__ 和 __iter__ 方法的就是迭代器 —— 迭代器協議 可以被for迴圈的都是可迭代的 可迭代的內部都有 __iter__ 方法 只要是迭代器,一定是可迭代的 可迭代的 .__iter__() 方法就可以得到一個迭代器 迭代器中的 __next__() 方法可以一個一個的獲取值
for 迴圈
for 迴圈其實就是在使用迭代器
只有 是可迭代對象的時候 才能用 for
當我們遇到一個新的變數,不確定能不能 for 迴圈的時候,就判斷它是否可迭代(判斷它內部有沒有 __iter__ )
for 迴圈的運行邏輯
for i in l: pass iterator = l.__iter__() iterator.__next__()
迭代器的好處
從容器類型中一個一個的取值,會把所有的值都取到。
節省記憶體空間(因為它不是一下子發內容存到記憶體當中的,而是一條一條的讀取)
迭代器並不會在記憶體中再占用一大塊記憶體,
而是隨著迴圈 每次生成一個
每次 next 每次給我一個
裝飾器複習
裝飾器
裝飾器的作用:在不改變原來函數的調用方式的情況下,在這個函數的前後添加新的功能
完美的符合了一個開發原則:開放封閉原則
對擴展的是開放的
對修改是封閉的
基礎的裝飾器
from functools import wraps def wrapper(func): @wraps(func) def inner(*args,**kwargs): '''在函數被調用之前添加的代碼''' ret = func(*args,**kwargs) # func是被裝飾的函數 在這裡被調用 '''在函數被調用之後添加的代碼''' return ret return inner #使用 —— @wrapper @wrapper def func(): #inner pass
帶參數的裝飾器
@wrapper -- > @warapper(argument) #三層嵌套函數 def outer(形參): def wrapper(func): def inner(*args,**kwargs): '''在函數被調用之前添加的代碼''' ret = func(*args,**kwargs) # func是被裝飾的函數 在這裡被調用 '''在函數被調用之後添加的代碼''' return ret return inner return wrapper @outer(True) def func(): pass
多個裝飾器裝飾一個函數
def wrapper1(func): @wraps(func) def inner(*args,**kwargs): print('before 1') print('******') ret = func(*args,**kwargs) # func是被裝飾的函數 在這裡被調用 '''在函數被調用之後添加的代碼''' return ret
def wrapper2(func): @wraps(func) def inner(*args,**kwargs): print('before 2') ret = func(*args,**kwargs) # func是被裝飾的函數 在這裡被調用 '''在函數被調用之後添加的代碼''' return ret @wrapper1 @wrapper2 def func(): print('111')