經驗拾憶(純手工)=> Python三器

可迭代對象、生成器、迭代器三者的關係

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的 三器、三程 是相對困難 又 頗有含金量的東西。
當時學的時候理解不了,特別是裝飾器,即便理解了,當時照着別人的例子去記憶和理解,事後也忘得很快,
當消化成本身的東西,或者本身能爲這個語法舉個例子出來,那麼印象會很深入。

語法這東西就是長時間不用就會忘記,沒辦法,撿起來個十幾回-幾十次就順手了,都這樣過來的。
當你對複雜語法熟悉的時候,你能夠不用去從頭去理思路,而是腦中迅速就能夠閃現出它的流程。 
我感受我三器寫的比較全面了,若是有問題歡迎交流與改正!
相關文章
相關標籤/搜索