美文网首页
函数 -- 迭代器与生成器

函数 -- 迭代器与生成器

作者: __深蓝__ | 来源:发表于2019-01-08 10:59 被阅读0次
迭代
  • 我们可以通过for循环来遍历listtuple,这种遍历称为迭代(Iteration)
>>> list = ['Google', 'Neuedu', 'Taobao', 'Baidu']
>>> for x in list:
        pass
>>> tup = ('Google', 'Neuedu', 'Taobao')
>>> for x in tup:
...     pass
>>> for x, y in [(1, 1), (2, 4), (3, 9)]:
...     print(x, y)
  • for循环不仅可以用在listtuple上,只要是可迭代对象,都可以迭代。
>>> dict = {'a': 1, 'b': 2, 'c': 3}
>>> for key in dict :  # 默认情况下迭代的是key
...     pass

>>> for value in dict.values() :  # 迭代value
...     pass

>>> for k, v in dict.items() :  # 同时迭代key和value
...     pass
>>> for x in 'abcdef':
...     pass 
  • 判断是否为可迭代对象,使用isinstance函数,将对象collections模块的Iterable类型进行比较来判断
>>> from collections import Iterable
>>> isinstance('abc', Iterable)
True
>>> isinstance([1,2,3], Iterable)
True
>>> isinstance(123, Iterable)
False
  • 练习:使用迭代查找一个list中最小和最大值,并返回一个tuple:
def findMinAndMax(L):
    return (None, None)
# 测试
if findMinAndMax([]) != (None, None):
    print('测试失败!')
elif findMinAndMax([7]) != (7, 7):
    print('测试失败!')
elif findMinAndMax([7, 1]) != (1, 7):
    print('测试失败!')
elif findMinAndMax([7, 1, 3, 9, 5]) != (1, 9):
    print('测试失败!')
else:
    print('测试成功!')
迭代器
  • 迭代器是一个可以记住元素遍历位置的对象。
  • 迭代器有两个基本函数:iter()创建迭代器,next()迭代下一个元素。
  • 字符串列表元组字典集合都可用于创建迭代器:
  • 迭代器只能从集合的第一个元素开始访问,直到所有元素被访问完。
>>> list = [1, 2, 3]
>>> it = iter(list)     # 创建迭代器对象
>>> print( next(it) )   # 输出迭代器的下一个元素
1
>>> print( next(it) )
2
>>> print( next(it) )
3
>>> print( next(it) )  # 没有元素可迭代时报错
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
  • 迭代器对象通常使用for语句进行遍历:
>>> list = [1,2,3,4]
>>> it = iter(list)    # 创建迭代器对象
>>> for x in it:
...     print (x, end=" ")

1 2 3 4
列表生成式

一种用来创建list的表达式。

使用range()函数可以生成有序的数列列表

>>> list(range(1, 11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

要生成[1x1, 2x2, 3x3, ..., 10x10]怎么办,可以使用循环+range

>>> L = []
>>> for x in range(1, 11):
...    L.append(x * x)

但这种循环形式太繁琐,可以使用列表生成式来生成

>>> [ x * x for x in range(1, 11) ]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

编写列表生成式时,把要生成的元素x * x放到前面,后面跟for循环,就可以把list创建出来

  • for循环后面还可以加上if判断,对元素进行筛选
>>> [ x * x  for x in range(1, 11)  if x % 2 == 0 ]
[4, 16, 36, 64, 100]
  • for循环中同时迭代两个变量来生成list
>>> d = { 'x': 'A',  'y': 'B',  'z': 'C' }
>>> [ k + '=' + v  for k, v  in d.items() ]
['y=B', 'x=A', 'z=C']
  • 使用双层循环,可以生成全排列
>>> [m + n  for m in 'ABC'  for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
  • 可以调用函数调用方法来生成list
# 列出当前目录下的所有文件和目录名
>>> import os # 导入os模块,模块的概念后面讲到
>>> [d for d in os.listdir('.')] # os.listdir可以列出文件和目录
# 把一个list中所有的字符串变成小写
>>> L = [ 'Hello',  'World',  'IBM',  'Apple' ]
>>> [ s.lower()  for s in L ]
['hello', 'world', 'ibm', 'apple']
  • 练习:添加if语句保证列表生成式能正确地执行
L1 = ['Hello', 'World', 18, 'Apple', None]
L2 = ???

# 测试:
print(L2)
if L2 == ['hello', 'world', 'apple']:
    print('测试通过!')
else:
    print('测试失败!')
生成器
  • 通过列表生成式可以直接创建列表。但受到内存限制,列表容量是有限的。而且对于包含了100万个元素的列表,如果仅访问前面几个元素,那么后面的空间都被浪费了。
  • 如果列表元素可以按照某种算法推算出来,这样就不必创建完整的list,而在循环过程中不断推算出后续的元素,从而节省大量的空间。
  • Python中将这种边循环边计算的机制,称为生成器(generator)
创建生成器
  • 创建 生成器(generator),只要把列表生成式[]改成()即可。
>>> g = ( x * x for x in range(10) )  # 创建生成器
>>> type(g)
<class 'generator'>

创建生成器后,可以调用next()函数获得生成器的下一个返回值,因为生成器中保存的是算法,每次调用next(g)时就会计算下一个元素的值,直到没有更多元素时,抛出StopIteration的错误。

>>> next(g)
0
>>> next(g)
1

更通用的方法是使用for循环来迭代它,并且不需要关心StopIteration错误。

>>> g = ( x * x for x in range(10) )
>>> for n in g:
...     print(n)

当函数定义中包含yield时,函数将变成生成器

def odd():
    print('step 1')
    yield 1
    print('step 2')
    yield(3)
    print('step 3')
    yield(5)
>>> o = odd()
>>> next(o)
step 1
1
>>> next(o)
step 2
3
>>> next(o)
step 3
5
>>> next(o)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
  • odd不是普通函数,而是生成器,在执行过程中,遇到yield就中断,下次又继续执行。执行3次yield后,已经没有yield可以执行了,所以,第4次调用next(o)时报错。
  • 一定要给循环设置退出条件,否则会产生无限数列。

如果生成列表的算法比较复杂,无法使用列表生成式时,可以用函数来实现。

创建生成器的另一种方法,先定义函数,再将print(b)改为yield b

  • 斐波那契数列(Fibonacci)为例,
  • 定义函数
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print(b)
        a, b = b, a + b
        n = n + 1
    return 'done'

>>> type(fib)
<class 'function'>
  • print替换为yield
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b           # 将print改为yield
        a, b = b, a + b
        n = n + 1
    return 'done'

>>> f = fib(6)
>>> type(f)
<class 'generator'>
>>> f = fib(6)
>>> next(f)
1
>>> next(f)
1
>>> next(f)
2
>>> next(f)
3
>>> next(f)
5
>>> next(f)
8
>>> next(f)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration: done
  • 使用for循环迭代生成器
>>> for x in fib(6):
...     print(x)
...
1
1
2
3
5
8
生成器执行过程
  • 生成器函数的执行流程不一样。
  • 函数是顺序执行,遇到return语句或最后一行语句时返回。
  • 生成器是在每次调用next()时候执行,遇到yield语句时返回,当再次调用next()时,从上次返回的yield语句处继续执行。
可迭代对象和迭代器
  • 可以直接作用于for循环的对象称为可迭代对象(Iterable)
  • 可以被next()函数调用,并返回下一个值的对象称为迭代器(Iterator)
  • 生成器(generator)迭代器(Iterator)
  • listdictstr等是可迭代对象(Iterable)但不是迭代器(Iterator)
  • 迭代器(Iterator)计算是 惰性 的,只有需要返回下一个数据时才会计算
  • 练习:把每行看做一个list,编写生成器输出下一行杨辉三角形
          1
         / \
        1   1
       / \ / \
      1   2   1
     / \ / \ / \
    1   3   3   1
   / \ / \ / \ / \
  1   4   6   4   1
 / \ / \ / \ / \ / \
1   5   10  10  5   1
def triangles():
    pass
# 期待输出:
# [1]
# [1, 1]
# [1, 2, 1]
# [1, 3, 3, 1]
# [1, 4, 6, 4, 1]
# [1, 5, 10, 10, 5, 1]
# [1, 6, 15, 20, 15, 6, 1]
# [1, 7, 21, 35, 35, 21, 7, 1]
# [1, 8, 28, 56, 70, 56, 28, 8, 1]
# [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
n = 0
results = []
for t in triangles():
    print(t)
    results.append(t)
    n = n + 1
    if n == 10:
        break
if results == [
    [1],
    [1, 1],
    [1, 2, 1],
    [1, 3, 3, 1],
    [1, 4, 6, 4, 1],
    [1, 5, 10, 10, 5, 1],
    [1, 6, 15, 20, 15, 6, 1],
    [1, 7, 21, 35, 35, 21, 7, 1],
    [1, 8, 28, 56, 70, 56, 28, 8, 1],
    [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
]:
    print('测试通过!')
else:
    print('测试失败!')




- end -

相关文章

网友评论

      本文标题:函数 -- 迭代器与生成器

      本文链接:https://www.haomeiwen.com/subject/nimqrqtx.html