如何理解Python迭代对象、迭代器、生成器等数据结构?
理解Python数据结构,有很多容器、可迭代对象(iterables)、迭代器、生成器、列表/集合/字典概念(列表、集合、听写理解)等概念混杂在一起,这难免让初学者一头雾水。在本文的帮助下,我试图阐明这些概念以及它们之间的关系。
Container(容器)
容器是一种将多个元素组织在一起的数据结构。容器的元素可以一一迭代得到。可以使用 in
, no 关键字
决定元素是否包含在容器中。通常,这样的数据结构将所有元素存储在内存中(有些特列并不将所有元素存储在内存中)。 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 in dict key 中的 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
尽管大多数容器都提供某种方式获取其中的每个元素,这不是容器本身提供的属性。 ,但是 Iterable 对象 赋予了容器这种能力。当然,并不是所有的容器都可以迭代,比如:布隆过滤器,虽然布隆过滤器可以用来检测某个元素是否在容器中,但是并不是每一个值都可以从容器中获取,因为布隆过滤器并不能根本不将元素存储在容器中,而是将它们描述为哈希函数的值并将其存储在表中。
可迭代对象(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'="">
这里 执行代码时: 实际执行的是: 提取此代码,你会看到解释器显示 命令❙GET_TERG。 那么什么是迭代器呢?它是一个有状态对象,当您调用 所以迭代器是一个实现工厂模型的对象,每次你请求下一个值时它都会返回给你。迭代器的例子有很多。例如,函数 创建无限序列: 从无限序列创建无限序列: 从无限序列创建有限序列: 要更直观地认识它的实现者,迭代器,作为斐波那契数列示例: Fib 既是可迭代对象(因为它实现了 更改下一次调用 迭代器就像一个延迟加载的工厂。在有人需要它并返回它之前,它不会创造任何价值。当没有被调用时,它处于睡眠模式并等待下一次调用。 生成器是Python语言最吸引人的功能之一。生成器实际上是一个特殊的迭代器,但是这个迭代器更加优雅。不再需要像上面的类一样编写方法 y。生成器必须是迭代器(反之则不然),因此任何生成器即使在延迟加载模式下也会生成值。使用生成器实现斐波那契数列的一个示例是: Generator 是 Python 中一个非常强大的编程结构。它可以用更少的中间变量编写流代码。此外,与其他容器对象相比,它可以节省内存和CPU。当然,它可以用更少的代码来实现类似的功能。现在您可以再次开始编辑代码。每当你看到类似: 之类的东西时,你都可以用生成器函数来替换它: 生成器表达式是列表推送。生成器版本看起来像列表理解,但它返回生成器对象而不是列表对象。 x
是一个可迭代对象 可迭代对象,可迭代对象是像容器一样的通俗叫法,并不指具体的数据类型。列表是可迭代对象,字典是可迭代对象,数组也是可迭代对象。 y
和 z
是两个独立的迭代器。迭代器保存一个状态。该状态用于存储当前迭代的位置,以便可以在下一次迭代期间检索它。正确的元素。迭代器具有特定的迭代器类型,例如 list_iterator
、set_iterator
。可迭代对象实现了 __iter__
和 __next__
(python3 中的next、‾ __next__),这两个方法对应池塘到内置功能
iter()
和 next()
。方法 __iter__
本身返回可迭代对象,使其既是可迭代对象又是迭代器。 x = [1, 2, 3]
for elem in x:
...
iter(x)
、FOR_ITER
指令是调用next()
当前调用的结果。 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]
方法,但无法在持续可见的元素上获取。指令,因为解释器已经对其进行了优化。
)对象都是迭代器,无论它如何实现。 >>> 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()
方法时,可以将下一个值返回到容器。任何__next__()
(在python2中实现)next()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
>>> 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
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]
__iter__
方法)又是迭代器(因为它实现了 __♷❓ex__❓ 方法)。实例变量 prev
和 curr
用户维护迭代器内的空间。每次调用 next()
方法时,都会完成两件事: next()
方法的状态并返回 Generator(生成器)
__iter__()
和__next__()
,只需要一个关键字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]
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
关键字。该值是一个生成器对象。当执行f=fib()
时,返回一个生成器对象。目前,函数体中的代码尚未执行。仅当显式或隐式调用以下内容时才会执行内部代码。 。 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
at 0x401f08>
>>> sum(a)
285
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。