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

可迭代对象

可迭代对象是迭代器、生成器、装饰器的基础,可迭代对象简单来说就是可以用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

猜你喜欢

转载自blog.csdn.net/qq_43299522/article/details/108950840
今日推荐