可迭代對象、生成器、迭代器三者的關係
1. 迭代器必定是可迭代對象
2. 生成器是迭代器的一種
3. 可迭代對象:必須實現 __iter__方法
4. 迭代器:必須實現 __iter__方法 和 __next__ 方法
5. 生成器:必須實現 __iter__方法 和 __next__ 方法,yield 代替了這兩個方法
6. 工具包: from collections import Iterable,Iterator
7. 查看抽象接口:
In [265]: Iterable.__abstractmethods__
Out[265]: frozenset({'__iter__'})
In [266]: Iterator.__abstractmethods__
Out[266]: frozenset({'__next__'})
8. 可迭代對象均可以被 for循環 所遍歷, 另外 實現了 __getitem__的類 其對象也可for遍歷
關於 __getitem__等魔法方法,之後會單獨寫一篇文章。
生成器
生成器有兩種寫法:
形式一:
In [230]: def f():
...: for x in range(10):
...: yield x
In [231]: y = f()
形式二:
In [239]: y = ( x for x in range(10) )
In [240]: y
Out[240]: <generator object <genexpr> at 0x0000024A4D3AFB48>
'推進生成器' 迭代有三種方式:
方式1:
In [232]: next(y)
Out[232]: 0
方式2:
In [233]: y.__next__()
Out[233]: 1
方式3:
In [236]: y.send(None)
Out[236]: 2
前兩種方式是等價的,第三種方式有些差異,且聽我說:
In [248]: def f():
...: print(1)
...: a = yield 2
...: print(a)
In [249]: y = f()
In [250]: y.send(None)
1 # 這裏是 print(1)的結果
Out[250]: 2
In [251]: y.send(100)
100 # 這裏是print(a)的結果
不知道閣下可否看出和兩種的差異:
1. yield 2 # 這行多了個賦值操做
2. send(100) # send() 函數裏面放了個,而後print(a) 打印的就是100
舉個例子:
1. a = yield 2 # 至關於一個士兵等待指令,等號左邊還未執行,程序就被封鎖了
2. send(100) # 長官輸入了一個100,士兵收到後就把程序解封,並執行等號左邊
3. 因而 就至關於 a 被賦值爲 100
迭代器
先看兩個簡單的函數:
In [294]: iter([1,2,3])
Out[294]: <list_iterator at 0x24a4e7f5780>
In [295]: reversed([1,2,3])
Out[295]: <list_reverseiterator at 0x24a4d38a6a0>
自定義迭代器:
In [283]: class A(Iterator): # 注意這裏:繼承了Iterator就不用實現 __iter__接口了
...: def __init__(self, value):
...: self.value = value
...: self.index = -1
...: def __next__(self): # 注意這裏:__next__是寫邏輯的主要接口,每次返回單個值
...: self.index += 1
...: return self.value[self.index]
In [284]: a = A(['Tom', 'Jerry'])
In [285]: next(a)
Out[285]: 'Tom'
In [286]: next(a)
Out[286]: 'Jerry'
串聯合並迭代器:
1. 普通序列迭代器串聯
In [287]: from itertools import chain
In [289]: chain(range(1),range(2),range(3))
Out[289]: <itertools.chain at 0x24a4d2c77f0>
In [290]: list(chain(range(1),range(2),range(3)))
Out[290]: [0, 0, 1, 0, 1, 2]
2. 字典序列迭代器串聯
In [288]: from collections import ChainMap
In [291]: ChainMap({1:1},{2:2,3:3})
Out[291]: ChainMap({1: 1}, {2: 2, 3: 3})
In [292]: dict(ChainMap({1:1},{2:2,3:3}))
Out[292]: {1: 1, 2: 2, 3: 3}
裝飾器
裝飾器若是按鑽牛角尖的方式來理解的確是很頭疼的事情。先說個例子吧:
In [296]: def f(func):
...: def f1():
...: print('原函數以前加點功能')
...: func() # 這就是原函數
...: print('原函數以後加點功能')
...: return f1
In [299]: @f #這句等價於=> func = f(func)
...: def func():
...: print('我是原函數哦')
In [300]: func()
>> 原函數以前加點功能
>> 我是原函數哦
>> 原函數以後加點功能
解釋
用大白話來說,裝飾器就是在原函數的先後加功能。也就是給原函數加個外殼。看上去是調用的原函數,
實則調用的是外殼函數,外殼函數裏面包括原函數和 一些其餘的自定義功能
例子:
麪包很差吃啊,可是你還想吃這個麪包,咋整? 上下抹點奶油,就變成了三明治(我沒吃過。。)
吃其餘麪包的時候繼續抹上奶油就好了(封裝性)
麪包-原函數
三明治-被裝飾器裝飾後的函數
各類功能(吃法)快速拼接:
@奶油
def 麪包1():
pass
@奶油
def 麪包2():
pass
@沙拉
def 麪包1():
pass
@沙拉
def 麪包2():
pass
存在問題:
@f #這句等價於=> func = f(func)
這是我上面說過的一句話,你仔細看看:
func以前指向的是(原函數)麪包的空間,恩,func函數名 也就是(__name__) 是func(麪包)
如今他指向的是(新函數)三明治的空間,恩,它的函數名是f1(三明治)
函數名變了,有些張冠李戴的感受,若是不想讓它變,並保持自己的函數名,看我操做:
In [301]: from functools import wraps
In [316]: def f(func):
...: @wraps(func) ### 沒錯 這裏是最主要的,基本格式固定寫法,照着寫便可
...: def f1():
...: print(func.__name__)
...: print('原函數以前加點功能')
...: func()
...: print('原函數以後加點功能')
...: return f1
標準裝飾器使用(原函數帶有返回值 和 參數):
In [323]: def f(func):
...: @wraps(func)
...: def f1(*args, **kwargs):
...: print('報年齡和性別~~~')
...: return func(*args, **kwargs)
...: return f1
In [324]: @f #每次都要記住 test=f(test) 這個隱含的變形,熟了就行了
...: def test(name,age):
...: print(name, age)
In [325]: test('Tom',18)
-------------------------------下面是打印部分
報年齡和性別~~~
Tom 18
帶參數的裝飾器:
構造以下:
def foo(arg): # 其實就是在原來的基礎上再再再次包裝個外殼,僅此而已
# 以前講的裝飾器f實現代碼原封不動全放在這裏面,看下面幫你寫好了。
def f(func):
@wraps(func)
def f1(*args, **kwargs):
return func(*args, **kwargs)
return f1
return f
調用形如:
@foo('123') #多了個參數,可分解爲foo的返回值f 放在 @ 的後面,是否是一切又回到了從前?
def func():
pass
裝飾類:
應用場景:
Python WEB框架 Flask/Django都有 FBV和CBV模式 (之後我也會寫這方面的文章)
FBV: Function Based View 簡單來講,邏輯視圖用函數來寫
----那麼須要裝飾器的時候,直接用普通函數裝飾器裝飾到函數上便可
CBV: Class Based View 簡單來講,邏輯視圖用類來寫
----這種沒有函數,是用類寫的,那麼這時候就須要對類進行裝飾了
核心思想:
仍是記住上面講的隱式變形,只不過此次傳給裝飾器的是類,對類的操做可就太多了。
裏面一大堆黑魔法,下面我就來寫一下類初始化功能性裝飾器。(之後也會單獨寫黑魔法的文章)
In [1]: def f(c):
def f1(*args, **kwargs):
return c(*args,**kwargs)
return f1
In [2]: @f
...: class A:
...: def __init__(self,name):
...: print(name)
In [3]: A('Tom')
Tom # 這是print打印的輸出
Out[3]: <__main__.A at 0x2948e8fb828>
類裝飾器:
這個場景真沒遇到過,不過實現也很容易的。
In [8]: class A:
...: def __init__(self,func):
...: self.func = func
...: def __call__(self,*args,**kwargs):
...: print('類裝飾填充了啊')
...: self.func(*args, **kwargs)
...:
In [9]: @A
...: def test():
...: print('我是原函數')
...:
In [10]: test()
類裝飾填充了啊
我是原函數
說明:後面關於類的裝飾器若是理解困難當作了解便可,用的也少。
結束語:
我一直認爲Python的 三器、三程 是相對困難 又 頗有含金量的東西。
當時學的時候理解不了,特別是裝飾器,即便理解了,當時照着別人的例子去記憶和理解,事後也忘得很快,
當消化成本身的東西,或者本身能爲這個語法舉個例子出來,那麼印象會很深入。
語法這東西就是長時間不用就會忘記,沒辦法,撿起來個十幾回-幾十次就順手了,都這樣過來的。
當你對複雜語法熟悉的時候,你能夠不用去從頭去理思路,而是腦中迅速就能夠閃現出它的流程。
我感受我三器寫的比較全面了,若是有問題歡迎交流與改正!