经验拾忆纯手工-Python三器

45次阅读

共计 4256 个字符,预计需要花费 11 分钟才能阅读完成。

可迭代对象、生成器、迭代器三者的关系

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 的 三器、三程 是相对困难 又 很有含金量的东西。当时学的时候理解不了,特别是装饰器,即使理解了,当时照着别人的例子去记忆和理解,过后也忘得很快,当消化成自己的东西,或者自己能为这个语法举个例子出来,那么印象会很深刻。语法这东西就是长时间不用就会忘记,没办法,捡起来个十几次 - 几十次就顺手了,都这样过来的。当你对复杂语法熟悉的时候,你可以不用去从头去理思路,而是脑中迅速就可以闪现出它的流程。我感觉我三器写的比较全面了,如果有问题欢迎交流与改正!

正文完
 0