Code前端首页关于Code前端联系我们

Python迭代对象、迭代器、生成器学习理解笔记

terry 2年前 (2023-09-25) 阅读数 52 #后端开发

理解Python数据结构时,容器(containers)、可迭代对象(iterables)、迭代器(iterators)、生成器(generators)、列表/集合/的很多字典概念理解(列表、集合、听写理解)混在一起,这肯定会让初学者感到困惑。我将用这篇文章来尝试解释这些概念以及它们之间的关系。

Python 迭代对象、迭代器、生成器学习理解笔记

Container(容器)

容器是一种将多个元素一起管理的数据结构。容器中的元素可以一次迭代获取一个。您可以使用 innot in 关键字来判断元素是否在容器内部。通常这种类型的数据结构将所有元素存储在内存中(也有一些特殊情况并非所有元素都存储在内存中,例如迭代器和生成器对象)。在 Python 中,常见的容器对象有:

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

容器比较很容易理解,因为你可以想到一个盒子,一个房子,或者一个壁橱,任何东西都可以被填充。从技术角度来看,当它可以用来询问某个元素是否存在时,那么该对象就可以被认为是一个容器。例如,列表、集合和元组都是容器对象:

>>> assert 1 in [1, 2, 3]      # lists
>>> assert 4 not in [1, 2, 3]
>>> assert 1 in {1, 2, 3}      # sets
>>> assert 4 not in {1, 2, 3}
>>> assert 1 in (1, 2, 3)      # tuples
>>> assert 4 not in (1, 2, 3)

询问是否包含元素 在 dict 中使用 dict 键:

>>> d = {1: 'foo', 2: 'bar', 3: 'qux'}
>>> assert 1 in d
>>> assert 'foo' not in d  # 'foo' 不是dict中的元素

询问子字符串是否在字符串中:

>>> s = 'foobar'
>>> assert 'b' in s
>>> assert 'x' not in s
>>> assert 'foo' in s 

虽然许多容器提供了多种方法检索 person 中的每个元素,这不是容器提供的功能。 ,但是可迭代对象为这种能力提供了一个容器。当然,并不是所有的容器都可以重复,比如:布隆过滤器,虽然可以利用布隆过滤器来检测容器中有元素,但是无法从容器中获取每个值,因为布隆过滤器并不能根本不将元素存储在容器中,而是通过哈希函数将它们映射到 Value 并将其存储在数组中。

可迭代对象(iterable)

正如我所说,许多容器都是可迭代对象。此外,还有其他对象也是可迭代对象,例如打开的文件、套接字等。任何能够产生 Iterator 的对象都可以称为可迭代对象。听起来可能有点令人困惑。没问题。我们先看一下例子:

>>> x = [1, 2, 3]
>>> y = iter(x)
>>> z = iter(x)
>>> next(y)
1
>>> next(y)
2
>>> next(z)
1
>>> type(x)
<class 'list'>
>>> type(y)
<class 'list_iterator'>

这里x是一个可迭代对象。可迭代对象(Iterable object),可迭代对象是容器之类的俗称,并不指代具体的数据类型。 List是一个可迭代对象,dict是一个可迭代对象,set也是一个可迭代对象。 yz 是两个独立的迭代器。迭代器有内部状态。该状态用于记录当前迭代的位置,以便在下一次迭代时检索它。正确的元素。迭代器有特定的迭代器类型,例如 list_iteratorset_iterator。可迭代对象实现方法 __iter__,该方法返回一个迭代器对象。

运行代码时:

x = [1, 2, 3]
for elem in x:
    ...

正确的执行是:
Python 迭代对象、迭代器、生成器学习理解笔记

反编译这段代码,可以看到解释器显式调用了指令“

  • ♸”,与调用一样指令iter(x)、FOR_ITER是调用next(),但是你不能直接从exit指令旁边的元素看到它,因为它是通过口译员。
    >>> import dis
    >>> x = [1, 2, 3]
    >>> dis.dis('for _ in x: pass')
      1           0 SETUP_LOOP              14 (to 17)
                  3 LOAD_NAME                0 (x)
                  6 GET_ITER
            >>    7 FOR_ITER                 6 (to 16)
                 10 STORE_NAME               1 (_)
                 13 JUMP_ABSOLUTE            7
            >>   16 POP_BLOCK
            >>   17 LOAD_CONST               0 (None)
                 20 RETURN_VALUE
    

    Iterator(迭代器)

    那么什么是迭代器呢?这是一个有状态对象,当您调用 next() 方法时,它可以返回容器中的下一个值。 __iter____next__() 对象(在 python2 中实现为 )的所有方法 __iter__ 的任何实现 返回自己的迭代器,__next__. 容器值中的下一项,如果容器中没有其他元素,则抛出 StopIteration 异常。他们如何实施并不重要。

    所以,迭代器是一个实现工厂模式的对象。每次您请求下一个值时,都会返回您。迭代器的例子有很多。例如,函数 itertools 返回一个迭代器对象。

    创建无限序列:

    >>> from itertools import count
    >>> counter = count(start=13)
    >>> next(counter)
    13
    >>> next(counter)
    14
    

    从完整序列创建无限序列:

    >>> from itertools import cycle
    >>> colors = cycle(['red', 'white', 'blue'])
    >>> next(colors)
    'red'
    >>> next(colors)
    'white'
    >>> next(colors)
    'blue'
    >>> next(colors)
    'red'
    

    从无限序列创建完整序列:

    >>> from itertools import islice
    >>> colors = cycle(['red', 'white', 'blue'])  # infinite
    >>> limited = islice(colors, 0, 4)            # finite
    >>> for x in limited:                         
    ...     print(x)
    red
    white
    blue
    red
    

    为了感受执行过程,我们配置一个 Iterator,将以斐波那契数列为例:

    class Fib:
        def __init__(self):
            self.prev = 0
            self.curr = 1
    
        def __iter__(self):
            return self
    
        def __next__(self):
            value = self.curr
            self.curr += self.prev
            self.prev = value
            return value
    
    >>> f = Fib()
    >>> list(islice(f, 0, 10))
    [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
    

    Fib 是一个可迭代对象(因为它实现了方法 __iter__)和迭代器(因为它实现了 __iter__)和迭代器(因为它实现了 ❀ ❀方法)。实例变量 prevcurr 用户维护迭代器中的状态。每次调用 next() 方法时,都会完成两件事:

    1. 更改下一次调用 next() 方法的状态 结果当前通话。

    交互器就像懒惰的工厂。直到有人需要它并返回它时,它才会返回值。当没有被调用时,它会处于休眠状态,等待下一次调用。

    Generator(生成器)

    生成器是Python语言最有趣的功能之一。生成器实际上是特殊的迭代器,但是这个迭代器更加优雅。不需要像上面的类那样写方法__iter__()__next__(),只需要一个”关键字 Generator必须是迭代器(下面是不正确的) ),所以任何生成器也会以延迟加载模式返回值。使用生成器实现斐波那契数列的一个示例是:

    def fib():
        prev, curr = 0, 1
        while True:
            yield curr
            prev, curr = curr, curr + prev
    
    >>> f = fib()
    >>> list(islice(f, 0, 10))
    [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
    

    fib 是一个常规的 Python 函数。其特别之处在于函数体中没有关键字return,函数Value的返回是一个生成器对象。执行f=fib()时,返回一个生成器对象。此时,函数体中的代码将不会被执行。里面的代码只有在下次显式或隐式调用时才会执行。 。

    生成器是 Python 中非常强大的编程结构。可以用更少的中间变量编写流代码。此外,与其他容器对象相比,它可以节省内存和CPU。当然,可以使用更少的代码来实现相同的功能。现在您可以开始重构您的代码。如果看到类似:

    def something():
        result = []
        for ... in ...:
            result.append(x)
        return result
    

    ,可以将其替换为生成器函数:

    def iter_something():
        for ... in ...:
            yield x
    

    生成器表达式(生成器表达式)

    生成器表达式是列表推送。生成器版本看起来像列表理解,但返回生成器对象而不是列表对象。

    >>> a = (x*x for x in range(10))
    >>> a
    <generator object <genexpr> at 0x401f08>
    >>> sum(a)
    285
    

    总结

    • 容器是元素的集合。 str、list、set、dict、file、sockets 对象都可以被视为容器,并且容器可以迭代(用于 for、while 等语句),因此称为可迭代对象。
    • 可迭代对象实现了__iter__方法,该方法返回一个迭代器对象。
    • Interators包含一个内部状态字段来记录下一次迭代的返回值。它实现了 __next____iter__ 方法。迭代器不会一次返回所有值。元素被加载到内存中,但结果会根据需要返回。生成器
    • 是一种特殊类型的迭代器。返回值不是通过return传递,而是通过yield传递。

  • 版权声明

    本文仅代表作者观点,不代表Code前端网立场。
    本文系作者Code前端网发表,如需转载,请注明页面地址。

    发表评论:

    ◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

    热门