python 面试必问题目 - 可迭代对象,迭代器,生成器

发布于 更新于
本文目录
  1. 1. 可迭代对象
  2. 1.1 如何判断一个对象是可迭代对象
  3. 1.2 迭代协议
  4. 2. 迭代器(Iterator)
  5. 2.1 如何判断一个对象是迭代器对象
  6. 2.2 迭代器协议
  7. 2.3 迭代器的作用
  8. 3、生成器
  9. 3.1、生成器表达式
  10. 3.2、生成器函数

技术文章测试开发

在python中我们可以通过for循环来遍历列表,元组,这种遍历我们称为迭代(Iteration)。在python中凡是可以通过for ... in进行迭代的对象,它就是可迭代对象。

1. 可迭代对象

在python中我们可以通过for循环来遍历列表,元组,这种遍历我们称为迭代(Iteration)。在python中凡是可以通过for ... in进行迭代的对象,它就是可迭代对象。

1.1 如何判断一个对象是可迭代对象

from collections.abs import Iterable
isinstance('abc', Iterable)

1.2 迭代协议

python中对象要提供迭代支持,必须定义一个__iter__方法,这个方法返回一个迭代器对象。

2. 迭代器(Iterator)

前面我们讲过可以通过for循环迭代的对象称为可迭代对象:Iteralbe。在底层for循环是先通过内置函数iter()将可迭代对象转换成一个迭代器,然后将其传入next()函数调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。

In [8]: ls = [1,2,3]
In [9]: ls = iter(ls)
In [10]: ls
Out[10]: <list_iterator at 0x225a33d8bb0>
In [11]: next(ls)
Out[11]: 1
In [12]: next(ls)
Out[12]: 2
In [13]: next(ls)
Out[13]: 3
In [14]: next(ls)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-14-2a26c7e4d7fa> in <module>
----> 1 next(ls)
StopIteration:

所以可以被next()函数调用并不断返回下一个值的对象称为迭代器:Interator。

2.1 如何判断一个对象是迭代器对象

from collections.abc import Iterator
ls = [1,2,3]
ls = iter(ls)
isinstance(ls, Iterator)

2.2 迭代器协议

迭代器对象自身需要支持以下两个方法,它们共同组成了迭代器协议:

  • iterator.__inter__()

    返回迭代器对象本身。

  • iterator.__next__()

    从对象中返回下一个值。如果没有可返回值,则引发StopIteration异常。

2.3 迭代器的作用

python中迭代器对象表示的是一个数据流,迭代器对象可以被next()函数不断调用返回下一个数据,直到没有数据抛出StopIteration异常。这个数据流像一个列表,但我们却不能提前知道它的长度,只能不断通过next()函数实现按需计算下一个数据,迭代器的计算是惰性的,只有在需要返回下一个数据时,它才会计算。

迭代器可以表示一个无限大的数据流,例如全体自然数,而使用list是永远不可能存储全体自然数的。

3、生成器

在python中创建元素数量巨大的列表不仅占用很大的内存,如果仅仅只需要访问某几个元素,那其他绝大多数元素占用的空间就白白浪费了。

所以,如果列表中的元素可以按照某种算法推算出来,那如果可以在循环的过程中不断推算出后续的元素,就可以不用创建完整的列表,从而节省大量的空间。

在python中,这种一边循环一边计算的机制,称为生成器(generator)

要创建一个generator,很多种方法。

3.1、生成器表达式

最简单的方式是把列表生成式的[]改成(),就创建了一个generator。

In [16]: g = (i**2 for i in range(10))

In [17]: g
Out[17]: <generator object <genexpr> at 0x00000225A239A510>

In [18]: next(g)
Out[18]: 0

In [19]: next(g)
Out[19]: 1

生成器也是迭代器,当然也可以通过for循环进行迭代。

In [20]: for item in (g):
    ...:     print(item)
    ...:
4
9
16
25
36
49
64
81

3.2、生成器函数

如果推算的算法比较复杂,用生成器表达式无法实现的时候,还可以通过函数来 实现。

比如,斐波拉契数列,除第一个和第二数外,任意一个数都等于前两个数的和:

0,1,1,2,3,5,8,...

def fib(n):
    a, b = 0, 1
    for _ in range(n):
    	print(a)
    	a,b = b, a+b
fib(10)
0
1
1
2
3
5
8
13
21
34

但是函数fib虽然包含了推算斐波拉契数列的算法规则,但是却是一次性算出了所有的值,和generator的逻辑很像。要将fib函数变成generator,只需要将print(a)改成yield a就可以了:

In [24]: def fib(n):
    ...:     a, b = 0, 1
    ...:     for _ in range(n):
    ...:         yield a
    ...:         a,b = b, a+b
    ...: g = fib(10)
    ...: g
Out[24]: <generator object fib at 0x00000225A3D4E9E0>

In [25]: next(g)
Out[25]: 0

In [26]: next(g)
Out[26]: 1

In [27]: for item in g:
    ...:     print(item)
    ...:
1
2
3
5
8
13
21
34

这就是定义generator的另一种方法。如果一个函数的定义中使用了yield关键字,那么这个函数就不再是一个普通的函数,而是一个生成器这时候调用函数返回的就是一个生成器对象

来源:testingpai

继续阅读