装饰器、迭代器、生成器详解

一、装饰器详解

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

  再举个栗子:装饰器本质是一个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,就能够理解为什么使用生成器比不适用生成器要好,能够理解使用生成器真的可以让代码变得清晰易懂。

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

原文地址:https://www.cnblogs.com/bingpan/p/8595459.html