0%

Python_可迭代对象,迭代器,生成器

在流畅的Python一书中,讲述的有些混乱,这里我自己也只是整理下.

来自于流畅的Python.

定义

  • 可迭代对象:实现了iter或者getitem方法的类实例,使用iter()内置方法可以获取可迭代对象.
  • 迭代器:一种惰性获取数据的方式,也就是一次按需要获得一个数据的方式;
  • 生成器: 生成器就是迭代器,所有的生成器都实现了迭代器的”接口”.而生成器能够”凭空”生成元素.

背景知识:序列可以迭代的原因:iter函数

解释器需要迭代对象x时,会自动调用iter(x)函数.

内置的iter函数作用:

  1. 检查是否实现了

    1
    __iter__方法,如果实现了就调用它,获取一个迭代器.
  2. 如果没有实现

    1
    __iter__方法,但是实现了__getitem__方法,Python会创建一个迭代器,尝试按顺序(从索引0开始)获取元素.
  3. 如果尝试失败则抛出TyprError异常,”object is not iterable”.

最简单的例子:实现 iter 函数

1
2
3
4
5
6
7
8
9
10
# 最简单的可迭代类定义
class Foo:
def __iter__(self):
pass
from collections import abc
print(issubclass(Foo , abc.Iterable))
# True
f = Foo()
print(isinstance(f , abc.Iterable))
# True

上面的例子我们发现当你实现了 iter ,系统已经默认你是Iter对象或者类.

当你仅实现了 getitem ,则系统并不人为你是可迭代对象或者类,但是我们已经知道即使只实现 getitem 也会返回一个迭代器.可以看下面的例子.

实现 getitem 函数的实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import re
import reprlib

RE_WORD = re.compile(r'\w+')

from collections import abc

class Sentence:

def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(text) # <1>

def __getitem__(self, index):
return self.words[index] # <2>

print(issubclass(Sentence , abc.Iterable))
# False
f = Sentence("as das d")
print(isinstance(f , abc.Iterable))
# False
for c in f:
print(c)
# as das d

可以看到上面的例子只实现了 getitem ,系统并没有认为其为可迭代对象,然而却能够进行迭代操作.

该如何检查对象可迭代?

从上面的原理与例子看,不能够通过isinstance或者issubclass等内置方法判定是否为可迭代对象,直接通过iter(x)即可.

可迭代对象与迭代器对比

定义细节

两者关系:Python可以通过可迭代对象获得迭代器

迭代器: 迭代器是这样一个对象,其实现了两个方法,

1
2
__next__():返回序列中下一个元素,如果没有则为StopIteration异常
__iter__():迭代器本身也可以迭代

可迭代对象并不是迭代器.

1
2
3
可迭代对象有一个__iter__方法,其作用是每次均实例化一个新的迭代器.
而迭代器需要实现上面说的两个方法.
故迭代器可以迭代,但是可迭代对象并不是迭代器.

迭代器的使用方式

最常见的for 调用迭代器:

1
2
3
4
S = "abc"
for c in S:
print(c)
# a b c

上面的for循环其实就是使用了迭代器实现的上面的操作.其等价于:

1
2
3
4
5
6
7
S = "abc"
it = iter(S)
print(next(it))
print(next(it))
print(next(it))
# a b c
# 上面的迭代器是无法还原的

两种可迭代对象的定义方式比较

我们上面知道了迭代器就是实现了next与iter两个方法的类,那么我们是单独构建迭代器对象还是在可迭代对象内部同时实现相关的方法呢?可以看下面的例子:

可迭代对象同时实现迭代器在内部:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import re
import reprlib

RE_WORD = re.compile(r'\w+')


class Sentence:

def __init__(self, text):
self.text = text

def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)

def __next__(self):
match = next(self.word_iter) # <4>
return match.group() # <5>

def __iter__(self):
return self

单独定义迭代器类

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
import re
import reprlib

RE_WORD = re.compile(r'\w+')


class Sentence:

def __init__(self, text):
self.text = text

def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)

def __iter__(self):
word_iter = RE_WORD.finditer(self.text) # <1>
return SentenceIter(word_iter) # <2>


class SentenceIter():

def __init__(self, word_iter):
self.word_iter = word_iter # <3>

def __next__(self):
match = next(self.word_iter) # <4>
return match.group() # <5>

def __iter__(self):
return self

很明显单独构建一个迭代器类更加合理,理由之一:为了支持”多种遍历”,必须能从一个可迭代的实例中获取多个独立的迭代器,而且各个迭代器能维护自身的内部状态.

生成器函数充当迭代器

上面提供的例子中单独定义了迭代器类,但是我们知道生成器也是迭代器,所以利用生成器也可以定义出可迭代对象.

生成器函数:关键字 yield

包含有yield关键字,该函数就是生成器函数.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def gen_123():
yield 1
yield 2
yield 3
print(type(gen_123))
# <class 'function'>
print(type(gen_123()))
# <class 'generator'>
g = gen_123()
print(next(g))
# 1
print(next(g))
# 2
print(next(g))
# 3

可以看到实例化后,为一个生成器,也就是迭代器.

生成器函数充当迭代器的应用:

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
import re
import reprlib

RE_WORD = re.compile(r'\w+')


class Sentence:

def __init__(self, text):
self.text = text # <1>

def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)

def __iter__(self):
for match in RE_WORD.finditer(self.text): # <2>
yield match.group() # <3>
S = Sentence("A D F G H")
for c in S:
print(c)
# A
# D
# F
# G
# H

生成器函数会创建一个生成器对象.并将生成器传给next()函数时,生成器会向前,执行下一个Yield语句,返回产出的值.知道迭代完成.

惰性实现迭代器

惰性实现就是不急于实现,生成器就是一种惰性实现.先对于一次性获得所有的元素,惰性实现会减少内存的消耗.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import re
import reprlib

RE_WORD = re.compile(r'\w+')


class Sentence:

def __init__(self, text):
self.text = text

def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
# re.finditer()返回的是一个迭代器,能够节省大量的内存
def __iter__(self):
for match in RE_WORD.finditer(self.text):
yield match.group()
S = Sentence("A B C D E")
for c in S:
print(c)
# A B C D E

其实这里在书中的意义更像是在过度.

生成器表达式

与列表表达式一样,这里可以使用生成式表达式,构造生成器.

1
(match.group() for match in re.finditer(self.text))

这里没有详细的写,也记不住.哟

总结

在书中作者还编写了其他的部分,例如标准库中的生成器函数,完整的实例应用以及其他的小细节.我这里仅仅对关于生成器的理论部分进行了论述,理解可迭代对象,迭代器,生成器的关系,以及基本的使用即可.