2017-10-03 10:12:01

python的for循环工作方式

当初学python时,感觉for语句和其他的语言没啥区别,但是当回过头来看,原来它并没有那么简单,理解它的工作方式对一些现象的理解很有帮助。虽然这篇文章的理解仍然很片面,但是应该也会有点帮助。

可迭代对象与迭代器

在说for循环之前不得不扯扯可迭代对象和迭代器的关系了,然后又可以扯扯生成器,比较简单,三句话就能说完了

  • 具有__iter__方法的对象都是可迭代对象,它会返回迭代器本身
  • 具有__next__方法的对象都是迭代器(在python2中是next方法)
  • 生成器是一种特殊的迭代器(为啥么特殊呢,还需要去理解下生成器是个什么东东)

不得不说说iter函数

函数原型:

iter(object[, sentinel])

然后再看看iter函数的官方说明吧。

Return an iterator object. The first argument is interpreted very differently depending on the presence of the second argument. Without a second argument, object must be a collection object which supports the iteration protocol (the iter() method), or it must support the sequence protocol (the getitem() method with integer arguments starting at 0). If it does not support either of those protocols, TypeError is raised. If the second argument, sentinel, is given, then object must be a callable object. The iterator created in this case will call object with no arguments for each call to its next() method; if the value returned is equal to sentinel, StopIteration will be raised, otherwise the value will be returned.

大致意思是说iter函数会返回一个迭代器,函数的解释分为两种情况:

  • 第二个参数不存在的情况,这种情况下object要么有__iter__方法,要么有__getitem__方法,如果都不满足,抛出TypeError异常
  • 第二个参数存在时,那么第一个参数必须有__next__方法,如果第一个参数调用__next__返回的值等于第二个参数,那么StopIteration异常会抛出

需要注意的是,for循环就是根据StopIteration来判断什么时候该正常的结束

怎么转换为迭代器

那么我们的序列/可迭代对象可以转换为迭代器吗?答案是肯定的,python3的很多内置函数都会返回迭代器。

可以转换为迭代器的对象分为两类:

  • 序列,序列是指可以用[]来访问的,也就是说它有一个__getitem__方法
  • 可迭代对象,具有__iter__方法的都是可迭代对象

再看看上面的iter函数的介绍,是不是神对应?没错,你可以认为序列/可迭代对象到迭代器的转换是通过iter函数直接或间接来实现的

for的工作方式

到这里一切就很清楚了,直接说说for循环的工作方式吧。

  1. 查找__iter__
  2. for...in...语句发现__iter__不存在时,就会调用iter函数来生成一个带有__iter__的对象?

虽然这样说可能会有点偏差,但是这么理解暂时不会有太大问题。

额外说明

加上这篇文章的说明可能会更好。

它的大致意思是通过手写一个支持for关键字迭代的类来说明它的工作方式

核心思想是:

  • __iter__返回自身的类实例即可
  • 类实现一个__next__方法

就像这样:

class MyTrain:
    def __iter__(self):
        return self
    def __next__(self):
        if not condition:
            raise StopIteration
        value = calculate next value
        return value # hand control over to the block

for name in MyTrain():
    do something with name

那么一个普通的序列应该怎么样实现这样一个东西呢?
很简单,在__next__方法中实现调用__getitem__(普通的序列必须有这样一个方法)来获取每一个值即可。

嗯,就酱~

Permanent link of this article:http://nulls.cc/post/for_statement_under_python

-- EOF --