清风白茶

我们都会上岸,阳光万里,去哪里都是鲜花开放。
梳理python的可迭代对象、迭代器、生成器

梳理python的可迭代对象、迭代器、生成器

在Python中这几个概念经常用到,但是也很容易被混淆。写这篇笔记,做个简单的总结。

说在前面的一些结论:

  1. 迭代器一定是一个可迭代对象,可迭代对象却不一定是迭代器。
  2. 可迭代对象可以通过Iter()函数转化为迭代器。
  3. 可迭代对象一定可以使用for循环迭代,能使用for循环迭代的不一定是可迭代对象

可迭代对象(Iterable)

先来看可迭代对象, 简单来说在python中的一个对象如果实现了__iter__()方法(这个方法的具体作用放到后面说),那个这个对象就是可迭代对象(Iterable)。例如常见的可迭代对象有:

  1. 集合或序列(如listtuplesetdictstr)
  2. 文件对象
  3. 实现了__iter__()方法的对象

来验证一下上述提到的是否真的是可迭代对象,并且是否都实现了__iter__()方法

使用python内置的isinstance可以用来验证对象是否是可迭代对象。使用内置hasattr函数来验证对象是否实现了__iter__方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from collections.abc import Iterable

# 验证列表
print(hasattr([], "__iter__")) # print true
print(isinstance([], Iterable)) # print true
# 验证元组
print(hasattr((), "__iter__")) # print true
print(isinstance((), Iterable)) # print true
# 验证集合
print(hasattr(set(), "__iter__")) # print true
print(isinstance(set(), Iterable)) # print true
# 验证字典
print(hasattr(dict(), "__iter__")) # print true
print(isinstance(dict(), Iterable)) # print true
# 验证字符创
print(hasattr("", "__iter__")) # print true
print(isinstance("", Iterable)) # print true
# 验证文件对象
with open("hello.py", "w") as f:
print(hasattr(f, "__iter__")) # print true
print(isinstance(f, Iterable)) # print true

# 我们自己来实现一个可迭代对象
class IterObj(object):
def __iter__(self):
# 注意 这只是一个例子,如果这个类的实例化对象拿到for循环里是会报错的,下面再讲为什么
return self


# 验证实现了 __iter__的对象
obj = IterObj()
print(isinstance(obj, Iterable)) # print true

迭代器(Iterator)

再来看一下迭代器的概念,简单来说,一个对象实现了 __iter____next__方法,那么它就是一个迭代器。例如我们在上一段代码中自己实现的可迭代对象Iterable只要再加上一个__next__方法,那么它的实例化对象就可以称为一个迭代器。

我们可以使用python的内置函数next对迭代器进行调用,只到迭代器抛出异常StopIteration。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class IterObj(object):
def __init__(self):
self.arr = [1, 2, 3]
self.length = len(self.arr)
self.i = 0

def __iter__(self):
return iter(self.arr)

def __next__(self):
while self.i < self.length:
self.i += 1
return self.arr[self.i - 1]
else:
self.i = 0
raise StopIteration()


obj = IterObj()
print(next(obj)) # print 1
print(next(obj)) # print 2
print(next(obj)) # pinrt 3
print(next(obj)) # 抛出异常

在上面的代码中我们在构造函数中定义了一个列表,在__iter__方法中我们返回了iter(self.arr)。那么这个iter()是个什么东西呢?

__iter__iter()

前面已经说过了,如果一个对象实现了__iter__方法,那么这个对象就是可迭代对象。那这个方法具体做了些什么呢?这个方法,返回的是一个迭代器(Iterator).

iter()这个内置函数返回的也是一个迭代器。它的工作过程是,先访问对象的__iter__方法,如果不存在则访问__getitem__方法。这就是结论2 的由来。

那这个Iter()是给谁用的呢?只有我们显式的调用吗?我们回顾一下对列表的一个常见的操作: 使用for循环对其进行迭代,在这个过程当中,for循环的工作过程是这样的:

  1. 先判断对象是否是为可迭代对象,或者对象是否实现了__getitem__()方法(先留个坑待会再填)。如果是的话,则对对象调用Iter方法,否则抛出异常。所以在我们自己实现的一个可迭代对象中,我们必须要保证__iter__这个方法返回的是一个迭代器
  2. 不断的调用迭代器里的__next__方法,返回迭代器里的一个值。
  3. 迭代的最后,迭代器里没有元素了,就会抛出一个异常,for循环结束。

对于其他的可迭代对象,过程也是一样的。弄明白for循环的工作原理后,就可以来动手实现一个可迭代对象了。

通常我们可以使用已有的可迭代对象来辅助我们实现自定义的可迭代对象, 也可以通过同时实现__next____iter__来自定义一个迭代器(Iterator)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 借助已知的可迭代对象来自定义可迭代对象
class MyList():
def __init__(self):
self.my_list = [1, 2, 3]

def __iter__(self):
return iter(self.my_list)


# 同时实现 `__next__`和`__iter__`来自定义一个迭代器(`Iterator`)。
class MyRange():
def __init__(self, num):
self.i = 0
self.num = num

def __iter__(self):
return self

def __next__(self):
if self.i < self.num:
res = self.i
self.i += 1
return res
else:
raise StopIteration

my_iterable = MyRange(10)
for i in my_iterable:
print(i) # 打印0-9

my_iterable = MyList()
for i in my_iterable:
print(i) # 打印 1-3

生成器(generator`)

生成器本质上还是一个迭代器。它也可以用在迭代的操作中,它和迭代器的区别是,它是一遍迭代一遍计算。在对生成器调用next()函数时,生成器才会计算需要返回的值,在这之前需要返回的值是不存在的,而迭代器在调用next()函数前,返回值已经存在了。

定义生成器有两种方式:

  1. 列表生成器
  2. 使用yield定义生成器函数

使用列表生成器

1
2
3
4
5
6
7
gen = (i*i for i in range(3)) # 需要把列表生成器的[]改为()
print(next(gen)) # print 0
print(next(gen)) # print 1
print(next(gen)) # pirnt 4
# 也可以使用for循环
for i in gen:
print(i)

使用yield关键字

举个栗子🌰,斐波那契数列:

1
2
3
4
5
6
7
8
9
def fib(n):
prev, curr = 0, 1
while n > 0:
n -= 1
yield curr
prev, curr = curr, curr + prev

print([i for i in fib(10)])
#[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

另外python使用生成器的特点实现了协程,它相对于线程处理高并发场景具有更大的优势。


评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×