zoukankan      html  css  js  c++  java
  • Python学习--07迭代器、生成器

    迭代

    如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration)。

    Python里使用for...in来迭代。

    常用可迭代对象有list、tuple、dict、字符串等。示例:
    list:

    for x in [1,2]:
    	print(x)
    
    for x,y in [(1,2),(3,4)]:
    	print(x,y)
    

    输出:

    1
    2
    1 2
    3 4
    

    上面的for循环里,同时引用了两个变量,在Python里是很常见的。

    tuple:

    for x in (1,2):
    	print(x)
    

    输出:

    1
    2
    

    dict:

    dict = {"name":"yjc", "age":18}
    for v in dict:
    	print(v, dict[v])
    
    for v in dict.values():
    	print(v)
    
    for k,v in dict.items():
    	print(k,v)
    

    输出:

    name yjc
    age 18
    
    yjc
    18
    
    age 18
    name yjc
    

    dict默认迭代的key,可以使用dict.values()获取value;还可以使用dict.items()同时获取key和value。

    字符串也是可迭代对象:

    for ch in 'abcd':
    	print(ch)
    

    输出:

    a
    b
    c
    d
    

    如果要对list实现类似Java那样的下标循环可以使用内置的enumerate函数——可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身:

    for k,v in enumerate([1,2]):
    	print(k,v)
    

    输出:

    0 1
    1 2
    

    那么,如何判断一个对象是否可迭代呢?可以使用collections模块里的Iterable判断:

    from collections import Iterable
    
    print(isinstance([1,2], Iterable));
    

    输出:

    True
    

    任何可迭代对象都可以作用于for循环,包括我们自定义的数据类型,只要符合迭代条件,就可以使用for循环。

    列表生成式

    列表生成式(List Comprehensions)是Python特有的用来创建list的生成式。

    示例:

    list = [x*x for x in range(1,10)]
    print(list)
    

    输出:

    [1, 4, 9, 16, 25, 36, 49, 64, 81]
    

    以上代码相当于:

    list = []
    for x in range(1,10):
    	list.append(x*x) 
    print(list)
    

    看到这里大家应该理解Python列表生成式的含义了。运用列表生成式,可以写出非常简洁的代码。

    我们再看几个示例:
    1)输出偶数:

    list = [x for x in range(1,10) if x % 2 == 0 ]
    print(list)
    

    输出:

    [2, 4, 6, 8]
    

    2)笛卡尔积

    a = "AB"
    b = "XYZ"
    list = [m+n for m in a for n in b]
    print(list)
    

    输出:

    ['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ']
    

    3)大写转小写

    list = [s.lower() for s in ['I', 'Love', 'Python', 100] if isinstance(s,str)]
    print(list)
    

    输出:

    ['i', 'love', 'python']
    

    这里使用isinstance()判断类型,因为非字符串类型没有lower()方法,Python会报错,所以这里加了个判断:

    >>> isinstance('love', str)
    True
    >>> isinstance(100, str)
    False
    

    生成器

    前面我们使用列表生成式可以很方便的生成一个我们需要的列表。但是如果生成一个很大的列表,会比较占内存,例如range(1,10000000),如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都浪费了。

    生成器(generator)不同于列表,它根据写好的算法,能够推算出下一个元素。生成器不属于list类型,属于generator类型。要创建一个生成器,第一种方法就是把列表生成式最外面的[]的改成()就行了:

    L = [x for x in range(1,10) if x % 2 == 0 ]
    print(type(L))
    print(L)
    
    g = (x for x in range(1,10) if x % 2 == 0 )
    print(type(g))
    print(g)
    

    输出:

    <class 'list'>
    [2, 4, 6, 8]
    
    <class 'generator'>
    <generator object <genexpr> at 0x01EAEE90>
    

    我们不能像list那样直接打印出generator。如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值:

    g = (x for x in range(1,10) if x % 2 == 0 )
    print(next(g))
    print(next(g))
    print(next(g))
    print(next(g))
    print(next(g))
    

    输出:

    2
    4
    6
    8
    Traceback (most recent call last):
      File "/1.py", line 6, in <module>
        print(next(g))
    

    generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

    我们可以使用for循环迭代generator:

    g = (x for x in range(1,10) if x % 2 == 0 )
    for x in g:
    	print(x)
    

    输出:

    2
    4
    6
    8
    

    for循环遇到StopIteration会停止迭代,不会扔出异常。

    下面,我们引入生成器的另外一种创建方法,使用yield关键字。函数里如果有yield关键字,那么它将不再是一个函数,而是一个生成器,遇到yield中断,下次又从上次中断的地方继续执行。示例:

    def test():
    	print('step 1:')
    	yield 11
    	print('step 2:')
    	yield 22
    	return 'ok'
    
    g = test()
    print(g)
    print(next(g))
    print(next(g))
    print(next(g))
    

    输出:

    <generator object test at 0x005BEC38>
    step 1:
    11
    step 2:
    22
    Traceback (most recent call last):
      File "D:UsersDesktop1.py", line 12, in <module>
        print(next(g))
    StopIteration: ok
    

    可以看到,test不是普通函数,而是generator,在执行过程中,遇到yield就中断,下次又继续执行。执行2次yield后,已经没有yield可以执行了,所以,第3次调用next(g)就报错。

    函数改成generator后,同样可以使用for循环迭代:

    for x in test():
    	print(x)
    

    下面是斐波拉契数列生成的函数,大家自行看有啥区别:

    def fib(n):
    	a,b,i=0,1,1
    	while i <= n:
    		a,b = b,a+b
    		i+=1
    		print(b)
    
    def gfib(n):
    	a,b,i=0,1,1
    	while i <= n:
    		a,b = b,a+b
    		i+=1
    		yield b
    
    fib(6)
    gfib(6)
    
    

    用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:

    >>> g = fib(6)
    >>> while True:
    ...     try:
    ...         x = next(g)
    ...         print('g:', x)
    ...     except StopIteration as e:
    ...         print('Generator return value:', e.value)
    ...         break
    ...
    g: 1
    g: 1
    g: 2
    g: 3
    g: 5
    g: 8
    Generator return value: done
    

    迭代器

    可以直接作用于for循环的一类是list、tuple、dict、set、str,另一类是generator。这些对象统称为可迭代对象:Iterable

    可以直接作用于next()的对象称为迭代器:Iterator。迭代器既可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。

    使用isinstance()判断一个对象是否是Iterable对象或者Iterator对象:

    >>> from collections import Iterable
    >>> isinstance([], Iterable)
    True
    
    >>> from collections import Iterator
    >>> isinstance((x for x in range(10)), Iterator)
    True
    

    生成器都是Iterator对象,但listdictstr虽然是Iterable,却不是Iterator

    把list、dict、str等Iterable变成Iterator可以使用iter()函数:

    >>> isinstance(iter([]), Iterator)
    True
    >>> isinstance(iter('abc'), Iterator)
    True
    

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

    for x in [1, 2, 3, 4, 5]:
        pass
    

    实际上完全等价于:

    # 首先获得Iterator对象:
    it = iter([1, 2, 3, 4, 5])
    # 循环:
    while True:
        try:
            # 获得下一个值:
            x = next(it)
        except StopIteration:
            # 遇到StopIteration就退出循环
            break
    
  • 相关阅读:
    浙大数据结构课后习题 练习二 7-2 Reversing Linked List (25 分)
    浙大数据结构课后习题 练习二 7-2 一元多项式的乘法与加法运算 (20 分)
    浙大数据结构课后习题 练习一 7-1 Maximum Subsequence Sum (25 分)
    浙大数据结构课后习题 练习一 7-1 最大子列和问题 (20 分)
    PAT Basic 1019 数字黑洞 (20 分)
    PAT Basic 1017 A除以B (20 分)
    PAT Basic 1013 数素数 (20 分)
    PAT Basic 1007 素数对猜想 (20 分)
    PAT Basic 1003 我要通过! (20 分)
    自动化运维——HelloWorld(一)
  • 原文地址:https://www.cnblogs.com/52fhy/p/6266940.html
Copyright © 2011-2022 走看看