简述

  1. 当我们在使用for循环时,即重复运行一个代码块,或者不断迭代容器对象中的元素,比如一些序列对象,列表,字典,元组,甚至文件等,而for循环的本质取出可迭代对象中的迭代器然后对迭代器不断的操作。
  2. 容器是一系列元素的集合,str、list、set、dict、file、sockets对象都可以看作是容器,容器都可以被迭代(用在for,while等语句中),因此他们被称为可迭代对象。
  3. 可迭代对象实现了__iter__方法,该方法返回一个迭代器对象。
  4. 迭代器持有一个内部状态的字段,用于记录下次迭代返回值,它实现了__next__和__iter__方法,迭代器不会一次性把所有元素加载到内存,而是需要的时候才生成返回结果。
  5. 生成器是一种特殊的迭代器,它的返回值不是通过return而是用yield。
    例如迭代文件:
    注:本文针对的示例是针对Python3,同时对Python2做了注释,注意区分自己使用的版本。
>>> for line in open("requirement.txt"):
...     print(line, end="")
...
Fabric==1.12.0
Markdown==2.6.7

for循环原理
可迭代对象需要实现__iter__方法,并返回一个迭代器,什么是迭代器呢?迭代器只需要实现 __next__方法。

>>> test=[23,12,45,12]
>>> obj=test.__iter__()#有此方法,说明列表是可迭代对象
>>> obj
<list_iterator object at 0x039983D0>
>>> obj.__next__()#有此方法,说明obj是迭代器
23
>>> obj.__next__()
12
>>> obj.__next__()
45
>>> obj.__next__()
12
>>> obj.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>

列表是一个可迭代对象,因为它实现了 __iter__方法,并且返回了一个迭代器对象(list_iterator),因为它实现了 __next__方法。我们看到它不断地调用__next__方法,其实就是不断地迭代获取容器中的元素,直到容器中没有更多元素抛出 StopIteration 异常为止。(在Python2中,实现迭代器变成了没有下划线的 next 方法)

  • 先判断对象是否为可迭代对象,不是的话直接报错,抛出TypeError异常,是的话,调用 __iter__方法,返回一个迭代器
  • 不断地调用迭代器的__next__方法,每次按序返回迭代器中的一个值
  • 迭代到最后,没有更多元素了,就抛出异常 StopIteration,这个异常 python 自己会处理,不会暴露给开发者

弄明白了 for 的执行原理之后,我们就可以实现自己的迭代器用在 for 循环中:
例1:

class Test:
    def __init__(self, number):
        self.i = 0
        self.number = number

    def __iter__(self):
        return self

    def __next__(self):
        if self.i < self.number:
            i = self.i
            self.i += 2
            return i
        else:#当self.i>=self.number时停止迭代
            raise StopIteration()


for j in Test(10):
    print(j)

例2:

class Account():
    def __init__(self,
                 account_name,
                 account_type,
                 account_cost,
                 return_amount=0):
        self.account_name = account_name  # 账户名
        self.account_type = account_type  # 账户类型
        self.account_cost = account_cost  # 月结费用
        self.return_amount = return_amount  # 返还金额


class AccountIterator():
    def __init__(self, accounts):
        self.accounts = accounts  # 账户集合
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.index >= len(self.accounts):
            raise StopIteration("到头了...")
        else:
            self.index += 1
            return self.accounts[self.index - 1]


accounts = [Account("张三", "年费用户", 450.00, 50),
            Account("李四", "月结用户", 100.00),
            Account("杨不悔", "月结用户", 190.00, 25),
            Account("任我行", "月结用户", 70.00, 10),
            Account("凌未风", "年费用户", 400.00, 40)]
for i in AccountIterator(accounts):
    print(i.account_name)
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐