本文为阅读《流畅的python》14章 总结
可能其中自己有理解错误的地方,希望给大佬指正
可迭代对象
迭代器
生成器
生成器表达式
yield from
iter()
可迭代对象
可以被内置函数iter处理的对象
内置iter函数会检查对象是否实现了__iter__方法,如果实现了是就调用它,获取一个迭代器
如果没有实现__iter__方法,但是实现了__getitem__方法,python会创建一个迭代器,尝试从索引0开始获取元素
如果以上都没有,则会抛出TypeError,‘X object is not iterable’
In [1]: s='abcdef'
In [2]: iter(s)
Out[2]: <str_iterator at 0x26631ddb978>
In [3]: c =1234
In [4]: iter(c)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-4-3b9b2877c4c0> in <module>
----> 1 iter(c)
TypeError: 'int' object is not iterable
所有可以看出s 字符串是可迭代对象,c 整数不是可迭代对象
可以迭代的对象就是可迭代对象,那什么是可迭代的?
继承或者实现了__getitem__方法或__iter__的对象,可以使用iter()测试
从 Python 3.4 开始,检查对象 x 能否迭代,最准确的方法是:调用 iter(x) 函数,如果不可迭代,再处理 TypeError 异常。这比使用 isinstance(x,abc.Iterable) 更准确,因为 iter(x) 函数会考虑到遗留的__getitem__ 方法,而 abc.Iterable 类则不考虑。
使用 iter 内置函数可以获取迭代器的对象。如果对象实现了能返回迭代器的__iter__ 方法,那么对象就是可迭代的。序列都可以迭代;实现了__getitem__ 方法,而且其参数是从零开始的索引,这种对象也可以迭代(《流畅的python》 )
迭代器
Python从可迭代对象中获取迭代器
In [8]: s='abcd'
In [9]: for char in s:
...: print(char)
a
b
c
d
In [18]: it = iter(s) # 可迭代对象构造迭代器
In [19]: while True:
...: try:
...: print(next(it)) # 不断的饰演那个next函数,获取下一个字符
...: except StopIteration: # 如果没有字符了,调用next(it) 或抛出StopIteration异常
...: break
a
b
c
d
In [20]: it
Out[20]: <str_iterator at 0x26631e91da0>
In [21]: next(it) # 此时已经没有了,在使用next(it)会抛出异常
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-21-bc1ab118995a> in <module>
----> 1 next(it)
StopIteration:
标准的迭代器有两个方法(缺一不可):
__ next __ :返回下一个可用元素·,没有元素会抛出StopIteration异常
__ iter__:返回self,以便在应该使用可迭代对象的地方使用迭代器,例如for循环中
因为迭代器只需 __ next__ 和 __ iter__ 两个方法,所以除了调用 next() 方法,以及捕获 StopIteration 异常之外,没有办法检查是否还有遗留的元素。此外,也没有办法“还原”迭代器。如果想再次迭代,那就要调用 iter(…),传入之前构建迭代器的可迭代对象。传入迭代器本身没用,因为前面说过 Iterator. __ iter__ 方法的实现方式是返回实例本身,所以传入迭代器无法还原已经耗尽的迭代器。
可迭代的对象一定不能是自身的迭代器。也就是说,可迭代的对象必须实现 __ iter__ 方法,但不能实现 __ next__ 方法。
另一方面,迭代器应该一直可以迭代。迭代器的 __ iter__ 方法应该返回自身。
生成器
普通的函数与生成器函数在句法上唯一的区别是,在后者的定义体中有 yield关键字。
只要 Python 函数中包含关键字 yield,该函数就是生成器函数。
In [3]: def gen():
...: yield 1
...: yield 2
...: yield 3
In [4]: gen()
Out[4]: <generator object gen at 0x000001D53835FB48> # 返回一个生成器对象
In [5]: for x in gen():
...: print(x)
1
2
3
In [6]: g =gen()
In [7]: next(g)
Out[7]: 1
In [8]: next(g)
Out[8]: 2
In [9]: next(g)
Out[9]: 3
In [10]: next(g) # 生成器函数的定义体执行完毕后,生成器对象会抛出 StopIteration 异常。
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-10-e734f8aca5ac> in <module>
----> 1 next(g)
StopIteration:
>>> def gen_1():
... print('start')
... yield 'A'
... print('continue')
... yield 'B'
... print('end')
...
>>> for c in gen_1():
... print(c)
...
start
A
continue
B
end
>>> g = gen_1()
>>> next(g) # 会打印 'start',然后停在第一个 yield 语句,生成值 'A'。
start
'A'
>>> next(g)
continue
'B'
>>> next(g) # 打印完end 之后到达函数末尾,生成器抛出StopIteration异常
end
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
生成器表达式
和列表推导式样子相似
In [1]: def gen_1():
...: ... print('start')
...: ... yield 'A'
...: ... print('continue')
...: ... yield 'B'
...: ... print('end')
...:
In [2]: res1 = [x*3 for x in gen_1()] # 列表推导式
start
continue
end
In [3]: res1
Out[3]: ['AAA', 'BBB']
In [4]: res2 = (x*3 for x in gen_1()) # 生成器推导式
In [5]: res2
Out[5]: <generator object <genexpr> at 0x00000156DA665F68>
In [6]: for x in res2:
...: print(x)
...:
start
AAA
continue
BBB
end
yield from
python3.3 新增yield from
>>> def chain(*iterables):
... for it in iterables:
... for i in it:
... yield i
...
>>> s = 'ABC'
>>> t = tuple(range(3))
>>> list(chain(s, t))
['A', 'B', 'C', 0, 1, 2]
######################################
>>> def chain(*iterables):
... for i in iterables:
... yield from i # yield from i 完全代替了内层的 for 循环。
...
>>> list(chain(s, t))
['A', 'B', 'C', 0, 1, 2]
iter() 函数
前面说使用iter(x)是可迭代对象变成迭代器
iter函数还可以出入两个参数,使常规的函数或任何可调用的对象创建迭代器,第一个参数必须是可调用的对象,用于不断调用(没
有参数),产出各个值;第二个值是哨符,这是个标记值,当可调用的对象返回这个值时,触发迭代器抛出 StopIteration 异常,而不产出哨符。
In [8]: from random import randint
In [9]: def d():
...: return randint(1,6)
...:
In [10]: d_iter = iter(d,1) # 返回一个 callable_iterator 对象。
In [11]: for roll in d_iter:
...: print(roll)
...:
2
6
5
3
3 # 不会打印 1,因为 1 是哨符