Python 迭代器。状态变量在 __init__ 还是 __iter__ 中初始化?

11 投票
3 回答
5684 浏览
提问于 2025-04-17 16:01

我对Python还比较陌生,最近在看一些关于定义迭代器对象的例子。

我看到的例子是:

class fibit:  # iterate through fibonacci sequence from 0,1...n<=max
    def __init__(self, max):       
        self.max = max

   def __iter__(self):
        self.a = 0
        self.b = 1
        return self

   def next(self):
        fib = self.a
        if fib > self.max:
            raise StopIteration
        self.a, self.b = self.b, self.a + self.b
        return fib

但是如果我把self.a和self.b的初始化从iter方法移到init方法,似乎(根据我简单的理解)效果是完全一样的。

class fibit:  # iterate through fibonacci sequence from 0,1...n<=max
    def __init__(self, max):
        self.a = 0
        self.b = 1        
        self.max = max

   def __iter__(self):
        return self

   def next(self):
        fib = self.a
        if fib > self.max:
            raise StopIteration
        self.a, self.b = self.b, self.a + self.b
        return fib

那么,哪种方式更推荐呢?

谢谢!:)

3 个回答

1

这其实要看你希望你的迭代器做什么。如果你有充分的理由需要多次遍历你的迭代器,并且每次都得到相同的结果,那么就把初始化放在 __iter__ 里;不过,这种情况应该是例外,而不是常态。因为正常的迭代器在用完后应该继续 raise StopIteration,而不是重新开始序列。实际上:一个在 StopIteration 被触发后还能返回更多值的迭代器是被认为有问题的(虽然这更多是提醒你在使用时要小心)。

所以,简单总结一下:

  • 对于标准迭代器,把初始化放在 __init__

  • 如果需要自定义或重复的行为,把初始化放在 __iter__

2

__init__ 是用来初始化一个类的实例的。简单来说,就是在创建这个类的对象时,用来设置一些属性。

__iter__ 是用来定义一个类的行为,使得这个类可以被迭代(也就是可以一个一个地取出值)。

@mgilson - __iter__ 通常会返回一个迭代器,这和列表是不同的(它们的类型在物理上是不同的),因为迭代器会逐个返回值,并且在返回后这些值就不再存在了。

试着调用一个 xrange 实例的 __iter__() 方法,然后尝试多次循环访问这些值。

 >>> foo = xrange(5)
 >>> bar = a.__iter__()
 >>> bar.next()
 0
 >>> bar.next()
 1
 >>> list(bar)
 [2, 3, 4]
11

初始化应该在 __init__ 里进行。这就是它存在的原因。

在Python中,迭代器对象通常是“一次性使用”的——也就是说,一旦你遍历过一个迭代器,就不应该再期待能再次遍历它。

所以,如果你尝试再次遍历这个对象,重新初始化值就没有意义了。为了说明这一点,我稍微扩展了一下你的代码:

class fibit_iter:  # iterate through fibonacci sequence from 0,1...n<=max
    def __init__(self, max):
        self.max = max

    def __iter__(self):
        self.a = 0
        self.b = 1
        return self

    def next(self):
        fib = self.a
        if fib > self.max:
            raise StopIteration
        self.a, self.b = self.b, self.a + self.b
        return fib

class fibit_init:  # iterate through fibonacci sequence from 0,1...n<=max
    def __init__(self, max):
        self.a = 0
        self.b = 1
        self.max = max

    def __iter__(self):
        return self

    def next(self):
        fib = self.a
        if fib > self.max:
            raise StopIteration
        self.a, self.b = self.b, self.a + self.b
        return fib

iter_iter = fibit_iter(10)
iter_init = fibit_init(10)

print "iter_iter"

for item in iter_iter:
    print item
    break

for item in iter_iter:
    print item
    break

print "iter_init"

for item in iter_init:
    print item
    break

for item in iter_init:
    print item
    break

基本上,我从你的初始化版本创建了一个对象,然后又从你的迭代版本创建了另一个对象。接着我尝试对这两个对象进行部分遍历两次。注意你会得到不同的结果:

iter_iter
0
0
iter_init
0
1

撰写回答