zoukankan      html  css  js  c++  java
  • 装饰器、迭代器、生成器详解

    一、装饰器详解

      举个栗子:内裤可以用来遮羞,但是到了冬天它没法为我们御寒,聪明的人们发明了长裤,有了长裤宝宝再也不冷了,装饰器就像长裤,在不影响内裤作用的前提下给我们的身子提供了保暖的功效。

      再举个栗子:装饰器本质是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。概括的将,装饰器的作用就是为已经存在的对象添加额外的功能。

      应用场景:插入日志、性能测试、事物处理、缓存、权限校验等。  

      1、简易无参装饰器:

     1 def user_logging(func):
     2     def wrapper(*args,**kwargs):
     3         print("这里写装饰器的逻辑代码")
     4         return func(*args,**kwargs)
     5     return wrapper
     6 
     7 def bar():
     8     print('i am bar')
     9 bar = user_logging(bar)
    10 bar()
    11 # 函数user_logging就是装饰器,它把执行真正业务方法的func包裹在函数里面,看起来像bar被user_logging装饰了。
    12 
    13 # 修饰后的版本(@符号是装饰器的语法糖,在定义函数的时候,避免再一次赋值操作)
    14 def user_logging(func):
    15     def wrapper(*args,**kwargs):
    16         print("这里写装饰器的逻辑代码")
    17         return func(*args)
    18     return wrapper
    19 
    20 @user_logging
    21 def foo():
    22     print('i am foo')
    23 
    24 @user_logging
    25 def bar():
    26     print('i am bar')
    27 bar()
    28 # 如上所示这样就可以省去bar=user_logging(bar)这一句,直接调用bar()即可得到想要的结果。如果还有其它类似的函数,直接调用,无序重复修改函数。
    29 # 这样不仅提高了程序的可重复利用性,并且增加了程序的可读性。

      2、有参装饰器: 

      在上面的装饰器调用中,比如@user_logging,该装饰器唯一的参数就是执行业务的函数。装饰器的语法允许在调用时提供其它参数,比如@decorator(a)。这样,就为装饰器的编写和使用提供了更大的灵活性。

     1 def user_logging(level):
     2     def decorator(func):
     3         def wrapper(*args,**kwargs):
     4             print('这里写装饰器的逻辑代码')
     5             return func(*args)
     6         return wrapper
     7     return decorator
     8 
     9 @user_logging(level='warn')
    10 def foo(name='foo'):
    11     print('i am %s'% name)
    12 foo()
    13 # 上面的user_logging是允许带参数的装饰器。它实际上是对缘由装饰器的一个函数封装,并返回一个装饰器。
    14 # 可以将它理解为一个含有参数的闭包,当使用@user_logging(level='warn')调用的时候,Python能够发现这一层的封装,并把参数传递到装饰器的环境中。

      3、类装饰器

      相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器还可以依靠类内部的__call__方法,当使用@形式将装饰器附加到函数上时,就会调用此方法。

     1 class Foo(object):
     2     def __init__(self,func):
     3         self.__func = func
     4         
     5     def __call__(self):
     6         print('class decorator runing')
     7         self.__func
     8         print('class decorator ending')
     9         
    10 @Foo
    11 def bar():
    12     print('bar')
    13     
    14 bar()
      4、内置装饰器:
          @staticmathod:中文博客英文文档
          @classmethod:中文博客英文文档
          @property:中文博客英文文档

    二、迭代器详解

      1、迭代器协议:

      由于生成器自动实现了迭代器协议,而迭代器对很多人来说,也是一个较为抽象的概念。未来路更好的理解生成器,先简单回顾一下迭代器协议的概念。

      a.迭代器协议是指:对象需要提供next方法,它要么返回迭代中的下一项,要么就引起一个Stoplteration异常,以上终止迭代;

      b.可迭代对象:实现了迭代器协议的对象;

      协议是一种约定,可迭代对象实现迭代协议,Python的内置工具(for,sum,min,max)等使用迭代器协议访问对象。

    三、生成器

      1、Python有两种不同的方式提供生成器:

        a.生成器函数:常规函数定义,但是使用yield语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行;

     1 # 使用生成器函数返回自然数的平方(注意返回的是多个值):
     2 # 使用生成器函数:
     3 def gensquares(N):
     4     for i in range(N):
     5         yield i**2
     6 
     7 for item in gensquares(5):
     8     print(item)
     9 
    10 # 使用普通函数:
    11 def gensquares(N):
    12     res = []
    13     for i in range(N):
    14         res.append(i*i)
    15     return res
    16 
    17 for item in gensquares(5):
    18     print(item)

        b.生成器表达式:类似于列表推导式,但是生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表。  

     1 # 使用列表推到,将会一次产生所有结果:
     2 squares = [x**2 for x in range(5)]
     3 print(squares)
     4 # [0, 1, 4, 9, 16]
     5 
     6 # 将列表推导式的中括号替换成圆括号,就是一个生成器表达式:
     7 squares = (x**2 for x in range(5))
     8 print(next(squares))    # 调用一次计算一次。
     9 print(next(squares))
    10 print(next(squares))
    11 print(next(squares))
    12 print(next(squares))

      2、深入了解生成器:

        1、语法上和函数类似:生成器函数和常规函数几乎是一样的。都使用def语句进行定义,差别在于,生成器使用yield语句返回一个值,而常规函数使用return语句返回一个值;

        2、自动是先迭代器协议:对于生成器,Python会自动实现迭代器协议,以便应用到迭代北京中(如for训话,sum函数)。由于生成器自动实现了迭代器协议,所以,调用next方法,并且。并且在没有值可以返回的时候,生成器自动产生 Stoplteration异常。

        3、状态挂起:生成器使用yield语句返回一个值。yield语句挂起该生成器的状态,保留足够的信息,一遍之后从它离开的地方继续执行。

        4、生成器只能被遍历一次;

        5、使用生成器与不使用生成器的区别:      

     1 # 假设有个需求是求一段文字中,每个单词出现的位置。
     2 # 不适用生成器
     3 def index_words(text):
     4     result = []
     5     if text:
     6         result.append(0)
     7     for index,letter in enumerate(text,1):
     8         if letter == '':
     9             result.append(index)
    10     return result
    11 
    12 # 使用生成器的情况:
    13 def index_words(text):
    14     if text:
    15         yield 0
    16     for index,letter in enumerate(text,1):
    17         if letter == '':
    18             yield index

      这里至少有两个充分的理由说明,使用生成器比不适用生成器代码更加清晰:

      1、使用生成器以后,代码行数更少;

      2、不适用生成器的时候,对于每次结果,首先看到的是result.append(index),其次才是index。每次看到的是一个列表的append操作,只是append的是我们想要的结果。使用生成器的时候,直接yield index,少了列表append操作的干扰,我们一眼就能够看出,代码是要返回index。

      这个例子充分说明, 合理使用生成器,能够有效提高代码可读性。只要完全接受了生成器的概念,理解了yield语句和return语句一样,也是返回一个值。MAME,就能够理解为什么使用生成器比不适用生成器要好,能够理解使用生成器真的可以让代码变得清晰易懂。

    个人备忘,如有错误还望指正。谢谢!

  • 相关阅读:
    Algs4-1.1.27二项分布
    Algs4-1.1.25数学归纳法证明欧几里得算法
    Algs4-1.1.26证明以下代码能够将a、b、c按照升序排列
    Algs4-1.1.24欧几里得算法求最大公约数
    Algs4-1.1.23区分在与不在白名单中的值
    Algs4-1.1.22以缩进方式打印递归参数
    微服务架构是什么?
    python 迭代器
    python 列表推导
    python 创建二维数组的方法
  • 原文地址:https://www.cnblogs.com/bingpan/p/8595459.html
Copyright © 2011-2022 走看看