Python的iterable与iterator

for ... in ...为核心的迭代语法是Python的常用语法之一.
iterable, 可迭代;iterator, 迭代器.
它们之间虽关系密切但不是同一个概念.

什么对象是可迭代的?

并不是每个对象都是可迭代的. 对不可迭代的对象应用迭代语法会报错.

class NotIterable(object):
    pass

not_iterable = NotIterable()

for i in not_iterable:
    pass
# iter(not_iterable) # 也会报相同的错误

输出:

Traceback (most recent call last):
  File ... in <module>
    iter(not_iterable)
TypeError: 'NotIterable' object is not iterable

那么, 如何判断一个对象是否可迭代呢?
Python的内置函数iter(obj)负责从可迭代对象生成迭代器. 它的工作流程为:
step 1. 先尝试调用obj__iter__方法, 如果有, 则根据它构建一个iterator对象并返回:

class MyIterable(object):
    def __iter__(self):
        print('{}.__iter__ is called'.format(self.__class__.__name__))
        return iter(range(10))
iterable = MyIterable()
iterator = iter(iterable)

输出:

MyIterable.__iter__ is called

step 2. 如果obj没有__iter__方法, 则尝试调用它的__getitem__方法, 如果有, 则根据它可以生成一个从位置0开始访问的迭代器并返回.

step3. 如果既没有__iter__方法, 也没有__getitem__, 则报错.

也就是说, 某个对象只要有__iter____getitem__方法, 它就是可迭代的.

什么是迭代器?

迭代器, 可以说是一种协议. 按照Python的风格, 实现某种协议并不需要implementsextends相应的接口或抽象类(见前文), 而是只需要实现必需的方法即可. 对于迭代器协议来说, 它的必需方法有两个:__iter____next__.

示例代码:

class MyIterable(object):
    def __init__(self, data):
        self.data = list(data)

    def __iter__(self):
        print('{}.__iter__ is called'.format(self.__class__.__name__))
        return MyIterator(self.data)# 返回一个iterator

class MyIterator(object):
    def __init__(self, data):
        self.data = data
        self.__idx = -1

    def __iter__(self):
        return self # 返回自身

    def __next__(self): #Python3
        self.__idx += 1
        if self.__idx >= len(self.data):
            raise StopIteration
        return self.data[self.__idx]
    def next(self): # Python2
        return self.__next__()

iterable = MyIterable(range(5))
iterator = iter(iterable)

for i in iterator:
    print(i)

输出:

MyIterable.__iter__ is called
0
1
2
3
4

有以下几点需要注意:
1. Python3需要__next__方法, 但Python2需要next方法, 只是命名不同.
2. iterableiterator对象都有__iter__方法, 但用途不一样. 前者返回一个新构建的iterator, 后者返回self(也是一个iterator对象).
3. iterator是有状态的. 示例中的状态变量为self.__idx, 用于存储当前的访问位置.
4. 当iterator.__next__方法抛出StopIteration信号时会停止迭代.

for... in iterable

其实我们很少会显式地调用iter方法将可迭代对象变成迭代器, 而是直接在可迭代对象上进行迭代, 例如, 上面的for i in ... 可以改写为:

iterable = MyIterable(range(5))
for i in iterable:
    print(i)

输出与之前一致.
可以看出, iterable对象在被迭代前已经被iter(iterable)包装成iterator, 只不过iter方法是由Python解释器替我们调用的.

iterator会保存访问状态

上面已经说过, iterator是有状态的, 这儿再通过一个例子更具体的说明它的状态性:

iterator = iter(iterable)
print("In iterator:")
for i in range(2):
    print(iterator.next())

iterator2 = iter(iterator)# 并不会创建一个新的iterator, 而是返回旧的iterator, 保持了它的访问状态.
print('In iterator2:')
for i in iterator2:
    print(i)
print(iterator is iterator2)

iterator3 = iter(iterable) # 创建一个新的iterator, 从头开始访问
print('In iterator3:')
for i in iterator3:
    print(i)

输出为:

MyIterable.__iter__ is called
In iterator:
0
1
In iterator2:
2
3
4
True
MyIterable.__iter__ is called
In iterator3:
0
1
2
3
4

总结

  1. 可迭代对象需要实现__iter__方法返回一个迭代器
  2. 迭代器需要实现__iter____next__ in Python3/ next in Python2.7. 前者返回self, 后者控制迭代输出.
  3. 迭代器一定是可迭代的, 但可迭代的不一定是迭代器.
  4. 迭代器有访问状态, 而可迭代对象没有.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值