Python 迭代器。状态变量在 __init__ 还是 __iter__ 中初始化?
我对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 个回答
这其实要看你希望你的迭代器做什么。如果你有充分的理由需要多次遍历你的迭代器,并且每次都得到相同的结果,那么就把初始化放在 __iter__
里;不过,这种情况应该是例外,而不是常态。因为正常的迭代器在用完后应该继续 raise StopIteration
,而不是重新开始序列。实际上:一个在 StopIteration
被触发后还能返回更多值的迭代器是被认为有问题的(虽然这更多是提醒你在使用时要小心)。
所以,简单总结一下:
对于标准迭代器,把初始化放在
__init__
里如果需要自定义或重复的行为,把初始化放在
__iter__
里
__init__
是用来初始化一个类的实例的。简单来说,就是在创建这个类的对象时,用来设置一些属性。
__iter__
是用来定义一个类的行为,使得这个类可以被迭代(也就是可以一个一个地取出值)。
@mgilson - __iter__
通常会返回一个迭代器,这和列表是不同的(它们的类型在物理上是不同的),因为迭代器会逐个返回值,并且在返回后这些值就不再存在了。
试着调用一个 xrange 实例的 __iter__()
方法,然后尝试多次循环访问这些值。
>>> foo = xrange(5)
>>> bar = a.__iter__()
>>> bar.next()
0
>>> bar.next()
1
>>> list(bar)
[2, 3, 4]
初始化应该在 __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