Python三大黑科技:迭代器、生成器、装饰器

    科技2024-06-14  75

    可迭代对象

    可迭代对象是迭代器、生成器、装饰器的基础,可迭代对象简单来说就是可以用for循环遍历的对象,比如常见的list,set和dict。

    一、迭代器

    对所有的可迭代对象调用 dir() 方法时,会发现他们都实现了 iter 方法。这样就可以通过 iter(object) 来返回一个迭代器。

    a = 'abcdefg' b = iter(a) # 创建迭代器对象 print(type(b)) # <class 'str_iterator'>

    可以看到调用 iter() 之后,变成了一个 str_iterator 的对象。会发现增加了 __next__方法。所有实现了 __iter__和__next__两个方法的对象,都是迭代器。

    迭代器是带状态的对象,它会记录当前迭代所在的位置,以方便下次迭代的时候获取正确的元素。__iter__返回迭代器自身,__next__返回容器中的下一个值,如果容器中没有更多元素了,则抛出StopIteration异常。

    # 迭代器 a = 'abcdefg' b = iter(a) # 创建迭代器对象 print(type(b)) while True: try: print(next(b)) except StopIteration: break

    Python的for循环本质上就是通过不断调用next()函数实现的。

    二、生成器

    生成器就是含有yield关键字的函数,是一种用普通语法定义的迭代器。

    # 生成器 def generator(): yield 'a' yield 'b' yield 'c' g = generator() print(type(g)) while True: try: print(next(g)) except StopIteration: break

    同样是通过 def 定义,然后通过 yield 来支持迭代器协议,所以比迭代器写起来更简单。

    进行函数调用的时候,返回一个生成器对象。在使用 next() 调用的时候,遇到 yield 就返回,记录此时的函数调用位置,下次调用 next() 时,从断点处开始。

    系统遇到 yield 关键词会将值返回,称为挂起,与return不同。

    def gen(n): while n > 0: print('Before') yield n n-=1 print('After') gg = gen(3) while True: try: print(next(gg)) print('-------------') except StopIteration: break

    输出结果:

    Before 3 ------------- After Before 2 ------------- After Before 1 ------------- After

    你完全可以像使用 iterator 一样使用 generator ,当然除了定义。定义一个iterator,你需要分别实现__iter__()方法和__next__()方法,但 generator 只需要一个小小的yield 。

    generator 还有 send() 和 close() 方法,都是只能在next()调用之后,生成器出去挂起状态时才能使用的。

    生成器在Python中是一个非常强大的编程结构,可以用更少地中间变量写流式代码,此外,相比其它容器对象它更能节省内存和CPU,当然它可以用更少的代码来实现相似的功能。现在就可以动手重构你的代码了,但凡看到类似:

    def something(): result = [] for ... in ...: result.append(x) return result

    就可以用生成器函数来代替:

    def iter_something(): for ... in ...: yield x

    提示:python 是支持协程的,也就是微线程,就是通过 generator 来实现的。配合 generator 我们可以自定义函数的调用层次关系从而自己来调度线程。

    栗子:斐波那契数列

    下面使用普通函数、迭代器、生成器来实现斐波那契数列

    def fab(max): x,y,z = 0, 0, 1 list = [] while x < max: list.append(y) y,z = z, x+y+z x += 1 return list

    iterator方法:

    class fab(object): ''' Iterator to produce Fibonacci ''' def __init__(self,max): self.max = max self.x = 0 self.y = 0 self.z = 1 def __iter__(self): return self def __next__(self): if self.x < self.max: r = self.z self.y,self.z = self.z,self.x + self.y + self.z self.x += 1 return r raise StopIteration('Done')

    为了节省内存,和处于未知输出的考虑,使用迭代器来改善代码,但是迭代器什么都好,就是写起来不简洁。所以用 yield 来改写第三版。

    Generator:

    def fab(max): x,y,z = 0,0,1 while x < max: yield z y,z = z,x+y+z x += 1 for x in fab(8): print(x)

    三、装饰器

    装饰器(Decorator)是python中最吸引人的特性,装饰器本质上还是一个函数,它可以让已有的函数不做任何改动的情况下增加功能。

    非常适合有切面需求的场景,比如权限校验,日志记录和性能测试等等。比如你想要执行某个函数前记录日志或者记录时间来统计性能,又不想改动这个函数,就可以通过装饰器来实现。

    1.定义:增强函数或类的功能的一个函数。 2.作用:增强函数的功能

    # 伪代码 def decorator(func): def wrapper(*args, **kwargs): # 可以自定义传入的参数 # *args:将函数传入的参数存储在元组类型的变量args中 # **kwargs:将函数的参数和值存储在字典类型的变量kwargs中 print(func.__name__) # 返回传入的方法名参数的调用 return func(*args, **kwargs) # 返回内层函数的函数名 return wrapper @decorator def f(): pass f()

    3.装饰器可以传参,也可以不传参

    # 装饰器 def decorator(func): # 嵌套函数 def wrap(*args, **kwargs): print('Start!') func(*args, **kwargs) # 类似于闭包,但是没有外部环境 print('End!') return wrap @decorator # 放在需要装饰的函数前 def f1(name): print('My name is ' + name) f1('hqh') # 调用方式不变

    实际上是对装饰器的一个函数封装,并返回一个装饰器。可以把它看成一个带参数的闭包。 有了装饰器,我们就可以剥离出大量与函数功能本身无关的代码,增加了代码的重用性。

    部分理论引用于:https://www.jianshu.com/p/efaa19594cf4

    Processed: 0.016, SQL: 8