Python 迭代器、可迭代对象、生成器、 __iter__详解

一. Python容器(container)

顾名思义,容器就是装东西的。Python容器就是装元素的,常见的容器有

  • list, deque,....
  • set, frozensets, ....
  • dict, defaultdict, OrderedDict, Counter, ....
  • tuple, namedtuple, …
  • str

容器中的元素可以逐个地迭代获取,可以用in, not in关键字判断元素是否包含在容器中。

for i in [1, 2, 3]:
print(i)

for i in "21yi.com":
print(i)

assert "." in "21yi.com"

但是!!

并不是所有容器都是可以迭代的(比如Bloom filter)。

大部分容器可以迭代,并非容器提供的能力,而是可迭代对象。

 

二. Python可迭代对象(iterable)

如上,很多容器都是可迭代对象,还有打开状态的files,sockets等等。

可迭代对象包括的部分常见对象:

  • 迭代器、生成器
  • 序列(字符串、列表、元组)
  • 字典
s = [1, 2, 3]
print(type(s))
print(iter(s))

s = set("21yi.com")
print(type(s))
print(iter(s))

返回结果:

<class 'list'>
<list_iterator object at 0x0000000001E05580>
<class 'set'>
<set_iterator object at 0x00000000022BE980>

这里的s和iter(s)都是可迭代对象。换句话说,只要是实现了__iter__方法的对象,都是可迭代对象。

当运行代码时,Python的实际执行情况:

for x in [1, 2, 3]:
print(i)

HTML 字符集

因为可迭代对象实现了__iter__方法,而__iter__方法必须返回一个迭代器。循环就这样执行了。

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

反编译代码,解释器调用GET_ITER指令,相当于调用iter(x),FOR_ITER指令就是调用next()方法,不断地获取迭代器中的下一个元素。

三. Python迭代器(iterator)

迭代器就是重复做事情,可以通俗理解为“循环”。Python 支持在容器中进行迭代。

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

iterator.__iter__() # 返回迭代器对象本身
iterator.__next__() # 从容器中返回下一项

简而言之,一个迭代器想被for ... in循环,就必须实现了__iter__和__next__方法

 

四. 可迭代对象和迭代器的区别

1. 主要区别

  • 可迭代对象包含迭代器
  • 如果对象有__iter__方法,它就是可迭代对象;如果对象拥有__next__方法,它就是是迭代器
  • 定义可迭代对象,必须实现__iter__方法
  • 定义迭代器,必须实现__iter__和__next__方法,因为迭代器也属于可迭代对象,必须实现__iter__方法
  需要实现的方法
可迭代对象 __iter__()
迭代器 __next__()、__iter__()

 

2. 判断一个对象,是不是可迭代对象或者迭代器?

s = "21yi.com"
print("__iter__" in dir(s)) # 是可迭代对象
print("__iter__", "__next__" in dir(s)) # 不是迭代器
print("__iter__", "__next__" in dir(s.__iter__())) # 是迭代器

3. 自定义一个可迭代对象和迭代器

class DemoIterable(object):  # 定义可迭代对象

def __init__(self, num):
self.num = num # 定义边界

def __iter__(self):
return DemoIterator(self.num) # 返回一个迭代器


class DemoIterator(object): # 定义迭代器类,其是MyList可迭代对象的迭代器类

def __init__(self, num):
self.now = 0 # 定义初始为0
self.num = num # 定义边界

def __iter__(self):
return self # 必须返回一个迭代器实例,因为自身就是迭代器,返回self

def __next__(self): # 迭代器必须实现__next__
while self.now < self.num:
self.now += 1
return self.now - 1 # 返回当前迭代值
raise StopIteration # 超出上边界,抛出异常


s = DemoIterable(10)
print(type(s)) #<class '__main__.DemoIterable'>

r = DemoIterator(10)
print(type(r)) #<class '__main__.DemoIterator'>
 

五. 生成器

生成器是一种特殊的迭代器,生成器自动实现了“迭代器协议”(即__iter__和__next__方法),不需要再手动实现两方法,只需要使用关键字yield就可以

def DemoFib():
prev, curr = 0, 1
while True:
yield curr
curr, prev = prev + curr, curr


f = DemoFib()
for i in range(3):
print(next(f))

print(dir(f)) # 可以看到,已经实现了__iter__和__next__方法