Python语言特性-迭代器和生成器

迭代器

定义:对于list、string、tuple、dict等这些容器对象,使用for循环遍历是很方便的。在后台for语句对容器对象调用iter()函数。iter()是python内置函数。
iter()函数会返回一个定义了next()方法的迭代器对象,它在容器中逐个访问容器内的元素。next()也是python内置函数。在没有后续元素时,next()会抛出一个StopIteration异常,通知for语句循环结束。
迭代器是用来帮助我们记录每次迭代访问到的位置,当我们对迭代器使用next()函数的时候,迭代器会向我们返回它所记录位置的下一个位置的数据。实际上,在使用next()函数的时候,调用的就是迭代器对象的_next_方法(Python3中是对象的_next_方法,Python2中是对象的next()方法)。所以,我们要想构造一个迭代器,就要实现它的_next_方法。但这还不够,python要求迭代器本身也是可迭代的,所以我们还要为迭代器实现_iter_方法,而_iter_方法要返回一个迭代器,迭代器自身正是一个迭代器,所以迭代器的_iter_方法返回自身self即可。

其中迭代器协议是指:对象需要提供next()方法,它要么返回迭代中的下一项,要么就引起一个异常,终止迭代。
可迭代对象实现了迭代器协议对象。像list, dict, tuple都是可迭代对象,可以通过内建函数iter() ,把这些都变成iterable(可迭代对象)。
for 循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,然后对回去到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束。

Python中一个实现了_iter_方法和_next_方法的类对象,就是迭代器。

下面我们看一个计算斐波那契数列的例子:


class Fib(object):
    def __init__(self, max):
        super(Fib, self).__init__()
        self.max = max
	
    def __iter__(self):
        self.a = 0
        self.b = 1
        return self
	def __next__(self):
		fib = self.a
		if fib > self.max:
			raise StopIteration
		self.a, self.b = self.b, self.a + self.b
		return fib

# 定义一个mian函数,循环遍历
def main():
	fib = Fib(10000)
	for i in fib:
		print(i)
	
if __name__ == '__main__':
    main()

解释说明
在本类的实现中,定义了一个_iter_(self)方法,这个方法是在for循环遍历时被iter()调用,返回一个迭代器。因为在遍历的时候,是直接调用的python内置函数iter(),由iter()通过调用_iter_(self)获得对象的迭代器。有了迭代器,就可以逐个遍历元素了。而逐个遍历的时候,也是使用内置的next()函数通过调用对象_next_(self)方法对迭代器对象进行遍历。所以要实现_iter_(self)和_next_(self)这两个方法。

总的来说就是在循环遍历自定义容器对象时,会使用python内置函数iter()调用遍历对象__iter__(self)获得一个迭代器,之后再循环对这个迭代器使用next()调用迭代器对象的__next__(self).
iter(self)只会被调用一次,而__next__(self)会被调用N次,直到出现异常。

生成器

生成器是一类特殊的迭代器,在循环遍历的时候会在需要的时候产生结果,即延迟操作。 生成器值遍历一次。

可以大概把生成器分为两类:
第一类:
生成器函数:即使用def定义函数,使用yield语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,一遍下次从它离开的地方继续执行。
下面我们继续用斐波那契数列来举例:


def Fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return ' 请添加数据'
# 调用方法,生成出10个数来
f=Fib(10)

# 使用一个循环捕获最后return 返回的值,保存在异常StopIteration的value中
while  True:
    try:
    	x=next(f)
        print("f:",x)
    except StopIteration as e:
        print("生成器最后的返回值是:",e.value)
        

第二类:
生成器表达式:类似于列表推导式,把[]换成()就可以了。 但是生成器表达式是按需产生一个生成器结果对象,想要拿到每一个元素,就需要循环遍历一次。
如下:

# 一个列表
xiaoke=[2,3,4,5]
# 生成器generator,类似于list,但是是把[]改为()
gen=(a for a  in xiaoke)
for  i  in gen:
    print(i)

使用生成器的主要目的就是为了提高效率。用生成器表达式取代列表推导式可以同时节省CPU的使用和内存的占用。

 # python内置的一些函数,可以识别这是生成器表达式,外面有一对小括号,就是生成器
 ret1=sum(a for a in range(10))
 print(ret1) 
  # 列表推导式 
 ret2=sum([a for a in range(10)])
  print(ret2)


猜你喜欢

转载自blog.csdn.net/weixin_42943975/article/details/83345204