有时候,我们会混淆Python中以下概念:
- container 容器
- iterable 可迭代
- iterator 迭代器
- generator 生成器
- generator expression 生成器表达式
- {list, set, dict} 列表、集合、字典

container 容器
容器是一种可以存放元素的数据结构,支持成员资格测试。容器存放于内存,通常内部的元素也存放于内存中。在 Python 中,一些众所周知的例子是:
- list, deque, …
- set, frozensets, …
- dict, defaultdict, OrderedDict, Counter, …
- tuple, namedtuple, …
- str
容器很容易掌握,因为它与现实生活中的一些容器类似:盒子、橱柜、房子、船等。
从技术上讲,当可以询问对象是否包含某个元素时,它就是一个容器。您可以对列表、集合或元组等执行此类成员资格测试:
assert 1 in [1, 2, 3] # 列表
assert 4 not in [1, 2, 3]
assert 1 in {1, 2, 3} # 集合
assert 4 in [1, 2, 3] # AssertionError
d = {1: 'foo', 2: 'bar', 3: 'qux'}
assert 1 in d
assert 4 not in d
assert 'foo' not in d # 'foo'不是字典中的_key_
s = 'foobar'
assert 'b' in s
assert 'x' not in s
assert 'foo' in s # 一个字符串“包含”它的所有子字符串
iterable 可迭代
如上所述,大多数容器可是迭代的,除此之外,还有很多对象是可迭代。
x = [1, 2, 3]
y = iter(x)
z = iter(x)
print(next(y)) # 1
print(next(y)) # 2
print(next(z)) # 1
print(type(x)) # <class 'list'>
print(type(y)) # <class 'list_iterator'>
通常,可迭代类自身便实现了__iter__()and __next__()方法,这使得类是可迭代及其自己的迭代器。当然,返回不同的对象作为迭代器也是完全可行的。
x = [1, 2, 3]
for elem in x:
...
以上代码实际运行过程:

当通过字节码指令分析这段Python 代码时,可以看到其显式调用了 GET_ITER,这本质上就是在调用iter(x)。FOR_ITER是一条相当于重复调用next()以获取每个元素的指令。但这不会显现在字节码指令中,因为它已针对解释器的速度进行了优化。
import dis
x = [1, 2, 3]
dis.dis('for _ in x: pass')
1 0 LOAD_NAME 0 (x)
2 GET_ITER
>> 4 FOR_ITER 4 (to 10)
6 STORE_NAME 1 (_)
8 JUMP_ABSOLUTE 4
>> 10 LOAD_CONST 0 (None)
12 RETURN_VALUE
iterator 迭代器
任何具有__next__()方法的对象都是迭代器,与内部元素的生成方式无关紧要。因此迭代器是一个“值”工厂,每次你向它询问“下一个”值时,它都知道如何计算它。
迭代器的例子不胜枚举。所有itertools函数都返回迭代器。有些产生的是无限序列:
from itertools import count
counter = count(start=13)
print(next(counter)) # 13
print(next(counter)) # 14
一些从有限序列产生无限序列:
from itertools import cycle
colors = cycle(['red', 'white', 'blue'])
print(next(colors)) # red
print(next(colors)) # white
print(next(colors)) # blue
print(next(colors)) # red
有些从无限序列产生有限序列:
from itertools import islice, cycle
colors = cycle(['red', 'white', 'blue'])
limited = islice(colors, 0, 4)
for x in limited :
print(x) # red white blue red
为了更好地了解迭代器的内部结构,让我们构建一个生成斐波那契数的迭代器:
from itertools import islice
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()
print(list(islice(f, 0, 10))) # [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
请注意,这个类既是可迭代的(因为它包含一个__iter__()方法),也是它自己的迭代器(因为它有一个__next__()方法)。
此迭代器中的状态完全保存在prev和curr实例变量中,并用于对迭代器的后续调用。每次调用next()都会做两件重要的事情:
- 为下一次调用修改状态;
- 为当前调用生成结果;
一个惰性的工厂
从外部看,迭代器就像一个惰性的工厂,它一直处于空闲状态,直到你向它请求一个值,它才会产生出一个值,之后它又会再次进入空闲状态。
generator 生成器
生成器是我最喜欢的 Python 功能。生成器是一种特殊的迭代器——它十分优雅。
生成器让你可以使用优雅简洁的语法编写迭代器,避免使用__iter__()和__next__()方法编写类。
让我们明确一点:
- 任何生成器也是一个迭代器(反之亦然!);
- 任何生成器都是一个懒惰地产生值的工厂;
通过生成器编写斐波那契序列:
from itertools import islice
def fib():
prev, curr = 0, 1
while True:
yield curr
prev, curr = curr, prev + curr
f = fib() # 此时不会执行代码:生成器最初以空闲状态开始
print(list(islice(f, 0, 10))) # [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
Types of Generators 生成器类型
Python 中有两种类型的生成器:生成器函数和生成器表达式。
- 生成器函数是关键字yield出现在其主体中的函数。我们刚刚看到了一个例子;
- 列表推导式(生成器等价物),其语法非常优雅;
numbers = [1, 2, 3, 4, 5, 6]
exp_1 = [x * x for x in numbers] # [1, 4, 9, 16, 25, 36]
exp_2 = {x * x for x in numbers} # {1, 4, 36, 9, 16, 25}
exp_3 = {x: x * x for x in numbers} # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36}
列表推导式的[]换成()就是生成器表达式:
numbers = [1, 2, 3, 4, 5, 6]
lazy_squares = (x * x for x in numbers)
print(lazy_squares) # <generator object <genexpr> at 0x0000022795774890>
print(next(lazy_squares)) # 1
print(list(lazy_squares)) # [4, 9, 16, 25, 36]
总结
生成器是一个令人难以置信的强大功能。它更高效地使用了内存和CPU,也需要更少的代码行。
建议:
def something():
result = []
for ... in ...:
result.append(x)
return result
将以上代码替换为:
def iter_something():
for ... in ...:
yield x
# def something(): # Only if you really need a list structure
# return list(iter_something())
补充
from collections.abc import *
class A:
def __next__(self):
return 1
class B:
def __iter__(self) :
return A()
a = A()
b = B()
print(isinstance(a, Iterable)) # False 因为其没有实现__iter__方法
print(isinstance(a, Iterator)) # False 因为其没有可迭代能力
print(isinstance(b, Iterable)) # True 因为其实现了__iter__方法
print(isinstance(b, Iterator)) # False 因为__next__方法非自己实现的
from collections.abc import Iterable
from collections.abc import Iterator
list_obj = [1, 2, 3]
list_obj_iter = list_obj.__iter__()
next(list_obj) # TypeError: 'list' object is not an iterator
print(next(list_obj_iter)) # 1
print(type(list_obj_iter)) # <class 'list_iterator'>
print(isinstance(list_obj, Iterable)) # True 实现了__iter__()
print(isinstance(list_obj, Iterator)) # False 未实现__next__()
print(isinstance(list_obj_iter, Iterable)) # True 实现了__iter__()
print(isinstance(list_obj_iter, Iterator)) # True 实现了__iter__()和__next__()
结论
- Iterator 一定是 Iterable
- Iterable 不一定是 Iterator
- 只要实现了
__iter__()方法,就是Iterable - 必须自己实现了
__iter__()和__next__(),才可以成为 Iterator
官方文档
iterable 可迭代
An object capable of returning its members one at a time. Examples of iterables include all sequence types (such as list, str, and tuple) and some non-sequence types like dict, file objects, and objects of any classes you define with an iter() method or with a getitem() method that implements Sequence semantics.
Iterables can be used in a for loop and in many other places where a sequence is needed (zip(), map(), …). When an iterable object is passed as an argument to the built-in function iter(), it returns an iterator for the object. This iterator is good for one pass over the set of values. When using iterables, it is usually not necessary to call iter() or deal with iterator objects yourself. The for statement does that automatically for you, creating a temporary unnamed variable to hold the iterator for the duration of the loop. See also iterator, sequence, and generator.
iterable 是一个能够一次返回其成员的对象。可迭代对象的示例包括所有序列类型(例如list、str和tuple)和一些非序列类型(例如dict、 文件对象、以及实现__iter__()或__getitem__()的类对象。
iterable 可用于for循环和其他需要序列的地方(zip(),map(), …)。当可迭代对象作为参数传递给内置函数iter()时,它会返回该对象的迭代器。使用迭代器时,通常不需要自己调用iter()或处理迭代器对象。for 语句会自动为你执行此操作,创建一个临时的未命名变量以在循环期间使用。
iterator 迭代器
An object representing a stream of data. Repeated calls to the iterator’s next() method (or passing it to the built-in function next()) return successive items in the stream. When no more data are available a StopIteration exception is raised instead. At this point, the iterator object is exhausted and any further calls to its next() method just raise StopIteration again. Iterators are required to have an iter() method that returns the iterator object itself so every iterator is also iterable and may be used in most places where other iterables are accepted. One notable exception is code which attempts multiple iteration passes. A container object (such as a list) produces a fresh new iterator each time you pass it to the iter() function or use it in a for loop. Attempting this with an iterator will just return the same exhausted iterator object used in the previous iteration pass, making it appear like an empty container.
iterator 是一种数据流对象。重复调用迭代器的__next__()方法(或将其传递给内置函数 next())返回连续的值。当没有更多数据可用时,会引发StopIteration异常。
迭代器需要有一个__iter__()返回迭代器对象本身的方法,因此每个迭代器也是可迭代的,并且可以在接受其他可迭代的大多数地方使用。每次将容器对象(如list)传递给iter()函数或在for循环中使用它时,它都会生成一个新的迭代器。
本文深入探讨Python中容器、可迭代对象、迭代器、生成器等概念的区别与联系,通过示例展示如何利用生成器简化代码并提高效率。

7644

被折叠的 条评论
为什么被折叠?



