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
    
  • 相关阅读:
    【SpringBoot】常用Starter介绍和整合模板引擎Freemaker、thymeleaf
    【SpringBoot】SpringBoot拦截器实战和 Servlet3.0自定义Filter、Listener
    【SpringBoot】单元测试进阶实战、自定义异常处理、t部署war项目到tomcat9和启动原理讲解
    【SpringBoot】SpringBoot热部署和配置文件自动注入实战
    IntelliJ IDEA备忘
    接口与抽象类
    泛型(11)-泛型与数组
    泛型(10)-泛型擦除与转换
    泛型(9)-泛型方法与方法重载
    泛型(8)-Java7的"菱形"语法与泛型构造器
  • 原文地址:https://www.cnblogs.com/52fhy/p/6266940.html
Copyright © 2011-2022 走看看