• Python3 迭代器和生成器


    想要搞明白什么是迭代器,首先要了解几个名词:容器(container)、迭代(iteration)、可迭代对象(iterable)、迭代器(iterator)、生成器(generator)。

    看图是不是更清楚点呢......

    一 容器(container)

    容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用innot in关键字判断元素是否包含在容器中。通常这类数据结构把所有的元素存储在内存中(也有一些特例,并不是所有的元素都放在内存,比如迭代器和生成器对象)在Python中,常见的容器对象有:

    • list, deque, ….
    • set, frozensets, ….
    • dict, defaultdict, OrderedDict, Counter, ….
    • tuple, namedtuple, …
    • str

    容器比较容易理解,因为你就可以把它看作是一个盒子、一栋房子、一个柜子,里面可以塞任何东西。从技术角度来说,当它可以用来询问某个元素是否包含在其中时,那么这个对象就可以认为是一个容器,比如 list,set,tuples都是容器对象。

    print( 1 in [1, 2, 3])      # lists
    # True
    print(4 not in [1, 2, 3])
    # True
    print(1 in {1, 2, 3})      # sets
    # True
    print(4 not in {1, 2, 3})
    # True
    print(1 in (1, 2, 3))      # tuples
    # True
    print(4 not in (1, 2, 3))
    # True
    
    # 询问某元素是否在dict中用dict的中key:
    
    d = {1: 'foo', 2: 'bar', 3: 'qux'}
    print(1 in d)
    # True
    print('foo' not in d)  # 'foo' 不是dict中的元素
    # True
    
    # 询问某substring是否在string中:
    
    s = 'foobar'
    print('b' in s)
    # True
    print('x' not in s)
    # True
    print('foo' in s)
    # True
    

    尽管绝大多数容器都提供了某种方式来获取其中的每一个元素,但这并不是容器本身提供的能力,而是可迭代对象赋予了容器这种能力,当然并不是所有的容器都是可迭代的,比如:Bloom filter,虽然Bloom filter可以用来检测某个元素是否包含在容器中,但是并不能从容器中获取其中的每一个值,因为Bloom filter压根就没把元素存储在容器中,而是通过一个散列函数映射成一个值保存在数组中。  

    二 迭代(iteration)

    什么是迭代,我的理解如下:

    • 第一,迭代需要重复进行某一操作

    • 第二,本次迭代的要依赖上一次的结果继续往下做,如果中途有任何停顿,都不能算是迭代

    下面来看几个例子,能更好理解迭代的含义。

    # 实例1
    # 非迭代
    count = 0
    while count < 10:
        print("hello world")
        count += 1
        
    # 实例2
    # 迭代
    count = 0
    while count < 10:
        print(count)
        count += 1
    

    实例1,仅仅只是在重复一件事,那就是不停的打印"hello world",并且,这个打印的结果并不依赖上一次输出的值。而实例2,就很好地说明迭代的含义,重复+继续。 

    三 可迭代对象 (iterable)

    通俗的说就是在每一种数据类型对象中,都会有有一个__iter__()方法,正是因为这个方法,才使得这些基本数据类型变为可迭代。 

    当我们运行以下代码的时候:

    x = [1,2,3]
    for elem in x:
         print(elem)
         
    # 运行结果:     
    # 1
    # 2
    # 3

    实际调用过程如下:

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

    from collections import Iterable
    
    print(isinstance('abc', Iterable)) # str是否可迭代
    # True
    print(isinstance([1,2,3], Iterable)) # list是否可迭代
    # True
    print(isinstance(123, Iterable)) # 整数是否可迭代
    # False  

    四 迭代器(iterator)

    通俗来讲任何具有__next__()方法的对象都是迭代器,对迭代器调用__next__()方法可以获取下一个值。

    五 生成器(generator)

    生成器是一个用简单的方式来完成迭代。简单来说,Python的生成器是一个返回可以迭代对象的函数。

    那要怎么创建生成器呢,很简单的,在一般函数中使用yield关键字,可以实现一个最简单的生成器,此时这个函数变成一个生成器函数。yieldreturn返回相同的值,区别在于return返回后,函数状态终止,而yield会保存当前函数的执行状态,在返回后,函数又回到之前保存的状态继续执行。

    看一下简单的生成器实例吧:

    def test():
        yield 1
        yield 2
        yield 3
    g=test()
    print('来自函数',g)
    print(g.__next__())
    print(g.__next__())
    
    # 运行结果
    # 来自函数 <generator object test at 0x000000000072B8E0>
    # 1
    # 2 

    生成器与一般函数有什么区别呢?

    • 生成器函数包含一个或者多个yield
    • 当调用生成器函数时,函数将返回一个对象,但是不会立刻向下执行
    • __iter__()__next__()方法等是自动实现的,所以我们可以通过next()方法对对象进行迭代
    • 一旦函数被yield,函数会暂停,控制权返回调用者
    • 局部变量和它们的状态会被保存,直到下一次调用
    • 函数终止的时候,StopIteraion会被自动抛出 

    来个例子看一下吧:

    # 简单的生成器函数
    def my_gen():
         n=1
         print("first")
         # yield区域
         yield n
    
         n+=1
         print("second")
         yield n
    
         n+=1
         print("third")
         yield n
    
    a=my_gen()
    print("next method:")
    # 每次调用a的时候,函数都从之前保存的状态执行
    print
    print(next(a))(next(a))
    print(next(a))
    
    # 运行结果
    # next method:
    # first
    # 1
    # second
    # 2
    # third
    # 3
    
    print("for loop:")
    # 与调用next等价的
    b=my_gen()
    for elem in my_gen():
        print(elem)
    
    # 运行结果
    # for loop:
    # first
    # 1
    # second
    # 2
    # third
    # 3
    

    来看看使用循环的生成器

    # 逆序yield出对象的元素
    def rev_str(my_str):
        length=len(my_str)
        for i in range(length-1,-1,-1):
            yield my_str[i]
    
    for char in rev_str("hello"):
        print(char)
    
    # 运行结果
    # o
    # l
    # l
    # e
    # h

    六 生成器表达式

    Python中,有一个列表生成方法,也就是常说的列表解析,提到列表解析就先要弄明白三元表达式的概念,什么是三元表达式呢?来个实例看看吧

    egg_list=[]
    for i in range(10):
        egg_list.append('鸡蛋%s' %i)
    print(egg_list)
    # ['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4', '鸡蛋5', '鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9']
    
    # 使用三元表达式替换如上代码
    l=['鸡蛋%s' %i for i in range(10)]
    print(l)
    # ['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4', '鸡蛋5', '鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9']
    
    l1=['鸡蛋%s' %i for i in range(10) if i > 5 ]
    print(l1)
    # ['鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9']
    
    # l2=['鸡蛋%s' %i for i in range(10) if i > 5 else i] #没有四元表达式
    # print(l2)
    
    l3=['鸡蛋%s' %i for i in range(10) if i < 5] 
    print(l3)
    # ['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4']

    了解了三元表达式,我们再来看看什么是生成器表达式,其实很简单,就是把三元表达式中的[]换成()即可。

    a=(x for x in range(10))
    b=[x for x in range(10)]
    # 这是错误的,因为生成器不能直接给出长度
    # print("length a:",len(a))
    
    # 输出列表的长度
    print("length b:",len(b))
    # length b: 10
    b=iter(b)
    # 二者输出等价,不过b是在运行时开辟内存,而a是直接开辟内存
    print(next(a))
    print(next(b)) 
    
  • 相关阅读:
    第一阶段冲刺第三天
    C语言printf函数求值顺序
    计算文件的MD5值
    SWFUpload后台取数据
    swfupload的使用方式
    文件上传的几种方式
    自定义缓存
    发邮件
    生成验证码
    将序列化成json格式后日期(毫秒数)转成日期格式
  • 原文地址:https://www.cnblogs.com/lvcm/p/9372622.html
Copyright © 2020-2023  润新知