python学习笔记_12(迭代器和生成器)

迭代器和生成器


1. 迭代器
1.1 迭代器规则
  • 迭代就是重复做一些事多次

  • 对象只要实现__iter__方法就可以对对象进行迭代,__iter__方法返回一个迭代器(iterator)

  • 迭代器就是实现了next方法,在调用next时返回下一个值,如果next调用了,但是迭代器没有值可以返回,引发一个StopIteration异常

    在python 3中,迭代器应该实现__next__方法,不是next,新增内建函数next访问此方法,next(it)等同与it.next()

  • 正式说法是一个实现了__iter__方法的的对象是可迭代的,一个实现了next方法的对象则是迭代器

  • 使用list可以显示把迭代器转换为列表

2. 生成器

生成器是python新引入的概念,也叫简单生成器,可以理解为迭代器的简单版本,迭代器工作在类级别,生成器工作在函数级别。

生成器是用普通函数语法定义的迭代器

2.1 创建生成器
  • 任何包含yield语句的函数称为生成器,定义生成器与普通函数基本相同,只需要吧return替换为yield语句
  • 生成器的行为和普通函数有很多区别,普通函数return后函数空间释放了,生成器yield产生一个新的值后函数会被冻结,停止yield语句,下次重新被唤醒后从上次yield的地方继续执行。
>>> def flatten(nested):
	for sublist in nested:
		for element in sublist:
			yield element

			
>>> nested = [['1', '2'], ['3', '4'], ['5', '6']]
>>> for num in flatten(nested):
	print num,

	
1 2 3 4 5 6
2.2 生成器推导式
  • 生成器推导式和列表推导式工作方式类似,只不过放回的不是列表而是生成器(并且不会立即进行循环),所返回的生成器允许一步一步的进行计算
  • 生成器推导式和列表推导式不同的是使用圆括号
  • 如果希望将可迭代对象”打包“(比如生成大量的值),那么最好不要用列表推导式,因为列表推导式会立即实例化一个列表,从而丧失迭代的优势
>>> g = ((i + 2) ** 2 for i in range(2, 27))
>>> g
<generator object <genexpr> at 0x03208C60>
>>> g.next()
16
>>> hasattr(g, '__iter__')
True
# 更妙的地方在于生成器推导式可以在当前的圆括号中直接使用,如在函数调用中可以不用增加另外一对圆括号
>>> sum(i ** 2 for i in range(10))
285
2.3 递归生成器
  • 如上面的展开嵌套列表的方式只能展开2层嵌套,如果需要处理更多情况,则需要更灵活的方案,使用递归
>>> def flatten(nested):
	try:
		for sublist in nested:
			for element in flatten(sublist):
				yield element
	except TypeError:
		yield nested

		
>>> list(flatten([[], [1, 2], [[3], [4]], 1]))
[1, 2, 3, 4, 1]

# 这么做有一问题,当nested是一个类似字符串的对象,那么它就不会引发TypeError,会出现无穷递归
# 解决这个问题需要在开始处增加一个检查语句,试着将传入对象和一个字符串进行拼接,看会不会有TypeError,这是检查一个对象是不是类似于字符串的最简单,最快速的方法
>>> def flatten(nested):
	try:
		try: nested + ''
		except TypeError: pass
		else: raise TypeError
		for sublist in nested:
			for element in flatten(sublist):
				yield element
	except TypeError:
			yield nested

			
>>> list(flatten([[], [1, 2], [[3], [4]], 1, '123']))
[1, 2, 3, 4, 1, '123']
>>> list(flatten([[], [1, 2], [[3], [4]], 1, '123', {'abc': 'val1'}]))
[1, 2, 3, 4, 1, '123', 'abc']
2.4 通用生成器
  • 生成器是一个包含yield关键字的函数,当它被调用是不会立即执行,而是返回一个迭代器,每请求一个值,就会执行生成器中的代码,直到遇到一个yield或return语句,yield语句意味着生产一个值,return语句意味着生成器要停止执行
  • 生成器由生成器的函数和生成器的迭代器两部分组成
  • 生成器的函数,用def语句定义的,包含yield的部分
  • 生成器的迭代器,调用生成器函数的放回值
2.5 生成器的方法
  • send

    • 外部作用域访问生成器的send方法和访问next方法一样,只不过send可以使用一个参数
    • 在内部则挂起生成器,yield作为表达式使用而不是语句使用,就是生成器重新运行时yield返回send的值,如果next被调用,则返回是None

    使用send方法只有生成器挂起后才有意义(也就是在yield函数第一次执行以后),如果在此之前需要给生成器更多信息,那么使用生成器函数参数

  • next,生成器产生一个新值

  • throw,生成器内引发一个异常,

  • close,停止生成器,close方法也是建立在异常的基础上,它在yield运行处引发一个GengeratorExit异常

>>> def repeater(value):
	while True:
		new = (yield value)
		print 'new is', new

		
>>> r = repeater(3)
>>> r.send('Hello')

Traceback (most recent call last):
  File "<pyshell#221>", line 1, in <module>
    r.send('Hello')
TypeError: can't send non-None value to a just-started generator
>>> r.next()
3
>>> r.send('Hello')
new is Hello
3
>>> r.send('43')
new is 43
3
>>> r.send('44')
new is 44
3
>>> r.next()
new is None
3
3. 八皇后问题
  • 八皇后问题是经典的回溯算法,使用生成器可以很好的解决

    #!/bin/sh
    # --*-- coding: utf-8 --*--
    
    # 1. 确定如何存储解决位置信息,根据问题分析,最终解决方案中每一行只能有一个皇后,只需存储没一行的皇后所在的列位置即可,使用元组state,其中state[i]表示第i行的皇后列的位置,起始位置为0
    
    # 2. 冲突判断,新加入一个皇后,判断是否会出现冲突
    # 在下一行的某一列中放置一个皇后,判断是否和已有的产生冲突
    
    def confict(state, nextX):
        """
        在下一行的nextY位置放置一个皇后,检测会产生冲突,遍历state中的每个皇后,判断是在同一列,或者行距离和列的距离相等
        """
        nextY = len(state)
        for y in range(nextY):
            if abs(state[y] - nextX) in (0, nextY - y):
                return True
        return False
    
    
    # 3. 扩展为任意皇后问题
    
    def queen(num=8, state=()):
        for pos in range(num):
            if not confict(state, pos):
                if len(state) == num - 1:
                    yield state + (pos,)
                else:
                    for res in queen(num, state + (pos,)):
                        yield (pos,) + res
                        
    # 4. 非递归方式
    def queen2(num = 4):
        states = [()]
        lst = range(num)
        while len(states[0]) < num:
            state = states.pop(0)
            for pos in lst:
                if not confict(state, pos):
                    states.append(state + (pos,))
        return states
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值