• 迭代器和生成器


    迭代: 访问集合元素的一种方式. 通常把使用for循环取数的过程称为遍历, 也叫迭代.

    可迭代对象(Iterable): 把可以通过for…in…这类语句迭代读取⼀条数据供我们使⽤的对象称之为可迭代对象.

    很多容器都是可迭代对象(iterable), 并不是所有容器都是可以迭代的;但凡可以返回一个迭代器的对象都是可迭代对象;

    容器: 这个称呼在其他博客上看到的, 了解下; 一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用in, not in关键字判断元素是否包含在容器中 . 常见的str, list, set, dict都是容器

    可以使用 isinstance() 判断一个对象是否是 Iterable 对象

    1
    2
    3
    4
    5
    6
    7
    8
    from collections import Iterable

    print(isinstance([], Iterable))
    print(isinstance((), Iterable)) # 判断元组
    print(isinstance({}, Iterable)) # 判断字典
    print(isinstance("", Iterable)) # 判断字符串
    print(isinstance(100, Iterable)) # 判断整数
    print(isinstance(False, Iterable)) # 判断boolean

    1.迭代器iterator

    迭代器: 任何实现了__iter____next__()(python2中实现next())方法的对象都是迭代器;

    __iter__返回迭代器自身,__next__返回容器中的下一个值,如果容器中没有更多元素了,则抛出StopIteration异常

    在遍历过程中就应该有一个“人”去记录每次访问到了第几条数据,以便每次迭代都可以返回下一条数据。我们把这个能帮助我们进行数据迭代的“人”称为迭代器(Iterator)。可迭代对象的本质就是提供一个这样的中间“人”即迭代器帮助我们对其进行迭代遍历使用。

    1. 可迭代对象的本质就是提供一个这样的中间“人”即迭代器帮助我们对其进行迭代遍历使用
    2. 可迭代对象是一个具备了__iter__方法的对象,通过__iter__方法获取可迭代对象的迭代器

    迭代器本质上是一个产生值的工厂,每次向迭代器请求下一个值,迭代器都会进行计算出相应的值并返回。

    可以通过iter()函数获取这些可迭代对象的迭代器, 然后可以对获取到的迭代器不断使用next()函数来获取下一条数据。

    1
    2
    3
    4
    5
    6
    7
    8
    li = [11, 22, 33, 44, 55]

    # 通过iter() 取得可迭代对象的迭代器
    iterator = iter(li)

    # 通过next()函数取得iterator迭代器指向的下一个值
    print(next(iterator))
    print(next(iterator))

    1.1 __iter____next__

    迭代器首先是一个可迭代对象, 可迭代对象必须实现__iter__方法

    迭代器同时要能够返回可迭代对象的下一个值,所以当调用next()函数的时候,需要能够返回值,所以必须定义next方法;

    1.2 for循环

    for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束。

    1.3 代码实例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    import collections


    class (object):
    def __init__(self):
    self.names = [] # 声明一个列表
    self.current = 0 # 记录迭代器迭代的位置, 默认是0 ,即从起始位置开始

    def add(self, name):
    self.names.append(name)

    def __iter__(self):
    """通过该方法取得迭代器对象"""
    return self

    def __next__(self):
    """取得下一个迭代的值"""
    if self.current < len(self.names):
    name = self.names[self.current]
    self.current += 1
    return name
    else:
    raise StopIteration


    # 创建MyClassmate实例
    my_classmate = MyClassmate()
    my_classmate.add("小王")
    my_classmate.add("小李")
    my_classmate.add("小张")

    # 测试MyList是不是可迭代对象
    print(isinstance(my_classmate, collections.Iterable))

    # 遍历数据
    for name in my_classmate:
    print(name)

    1.4 应用场景

    斐波拉契序列

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    class FibIterator(object):
    """斐波那契数列迭代器"""

    def __init__(self, n):
    """初始化属性"""
    self.n = n # 记录生成fibonacci的数列的个数
    self.current_index = 0 # 记录当前遍历的下标

    # 记录fibonacci数列前面的两个值
    self.num1 = 0
    self.num2 = 1

    大专栏  迭代器和生成器ion">def __next__(self):
    """被next()函数调用来获取下一个数"""
    if self.current_index < self.n:
    num = self.num1
    self.num1, self.num2 = self.num2, self.num1 + self.num2
    self.current_index += 1
    return num
    else:
    raise StopIteration

    def __iter__(self):
    """迭代器的__iter__返回自身即可"""
    return self


    if __name__ == '__main__':
    fib = FibIterator(10)
    for num in fib:
    print(" ", num, end="")

    运行结果: 0 1 1 2 3 5 8 13 21 34

    2.生成器generator

    生成器: 可以理解为一个特殊的迭代器。调用这个函数就得到一个迭代器,生成器中的yield相当于一个断点,执行到此返回一个值后暂停,从而实现next取值。

    • 任意生成器都是迭代器(反过来不成立)
    • 任意生成器,都是一个可以延迟创建值的工厂

    它不需要再像上面的类一样写__iter__()__next__()方法了,只需要一个yiled关键字。

    2.1 创建生成器方法

    2.1.1 把列表生成式的[] 改成()

    1
    2
    3
    4
    5
    6
    7
    8
    # 参考列表生成式
    L=[x*2 for x in range(6)]
    print(L) # 输出结果:[0, 2, 4, 6, 8, 10]

    # 把[] 改为() :就是一个简单的列表生成器
    G=(x*2 for x in range(6))

    print(G) # 输出的是生成器对象: <generator object <genexpr> at 0x7ff7f8bbd5c8>

    2.1.2 列表生成式

    用生成器实现斐波拉契序列

    1
    2
    3
    4
    5
    6
    7
    8
    def fib():
    """斐波拉契函数"""
    prev, curr = 0, 1
    while True:
    yield curr
    prev, curr = curr, curr + prev
    f = fib()
    print(list(islice(f,0, 10))) # 输出结果是[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

    fib就是一个普通的python函数,它特殊的地方在于函数体中没有return关键字,函数的返回值是一个生成器对象。当执行f=fib()返回的是一个生成器对象,此时函数体中的代码并不会执行,只有显示或隐示地调用next的时候才会真正执行里面的代码。

    另外一种写法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    def fib(n):
    curr_index = 0
    num1, num2 = 0, 1
    while curr_index < n:
    """
    1. 假如函数中有yield,则不再是函数,而是生成器
    2. yield 会产生一个断点
    3. 假如yield后面紧接着一个数据,就会把数据返回,
    作为next()函数或者for ...in...迭代出的下一个值
    """
    yield num1
    num1, num2 = num2, num1 + num2
    curr_index += 1

    if __name__ = '__main__':
    G = fib(5) # # 假如函数中有yield,则不再是函数,而是一个生成器
    #  生成器是一种特殊的迭代器
    for num in G
    print("", num, end="") # 输出0 1 1 2 3 5

    2.2 生成器总结

    • 使用了yield关键字的函数不再是函数,而是生成器。(使用了yield的函数就是生成器)
    • yield关键字有两点作用:
      • 保存当前运行状态(断点),然后暂停执行,即将生成器(函数)挂起
      • 将yield关键字后面表达式的值作为返回值返回,此时可以理解为起到了return的作用
    • 可以使用next()函数让生成器从断点处继续执行,即唤醒生成器(函数)

    2.3 使用send

    我们除了可以使用next()函数来唤醒生成器继续执行外,还可以使用send()函数来唤醒执行。使用send()函数的一个好处是可以在唤醒的同时向断点处传入一个附加数据。

    执行到yield时,gen函数会暂停,返回i的值; temp接收下次obj.send(“haha”)发送过来的值,next(obj)等价obj.send(None)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    def gen():
    i = 0
    while i < 5:
    temp = yield i
    print(temp)
    i += 1


    if __name__ == '__main__':

    obj = gen() # 取得生成器对象

    # 使用next()唤醒生成器
    print(next(obj))
    print(next(obj))

    # 使用send唤醒生成器 ,在唤醒的同时向断点处传入一个附加数据
    print(obj.send("haha"))

    # 使用next()唤醒生成器
    print(next(obj))

    # 使用send唤醒生成器 ,在唤醒的同时向断点处传入一个附加数据
    print(obj.send("python"))
  • 相关阅读:
    如何面试测试工程师?
    自动登录VSS
    软件质量浅谈
    如何在工作中更好的学习
    用例设计工具PICT — 输入组合覆盖
    如何提高送测版本的质量?
    Findbugs介绍及使用方法
    省市区三级联动(附数据库和导入模板的做法)
    EF-联合查询-结果集-Group by-统计数目
    WCF-复合类型使用;传输图片
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12255807.html
Copyright © 2020-2023  润新知