3

我试图弄清楚为什么下面的例子不起作用。

class BaseClass(object):
    def __init__(self):
        self.count = 1

    def __iter__(self):
        return self

    def next(self):
        if self.count:
            self.count -= 1
            return self
        else:
            raise StopIteration


class DerivedNO(BaseClass):
    pass


class DerivedO(BaseClass):
    def __init__(self):
        self.new_count = 2
        self.next = self.new_next

    def new_next(self):
        if self.new_count:
            self.new_count -= 1
            return None
        else:
            raise StopIteration


x = DerivedNO()
y = DerivedO()

print x
print list(x)
print y
print list(y)

这是输出:

<__main__.DerivedNO object at 0x7fb2af7d1c90>
[<__main__.DerivedNO object at 0x7fb2af7d1c90>]
<__main__.DerivedO object at 0x7fb2af7d1d10>
Traceback (most recent call last):
  File "playground.py", line 41, in <module>
    print list(y)
  File "playground.py", line 11, in next
    if self.count:
AttributeError: 'DerivedO' object has no attribute 'count'

如您所见,DerivedO当我尝试next()__init__. 这是为什么?对 next 的简单调用可以正常工作,但在使用迭代技术时根本不行。

编辑:我意识到我的问题并不完全清楚。AttributeError 不是我要解决的问题。但它确实表明它next()被调用BaseClass而不是DerivedO像我想象的那样被调用。

4

2 回答 2

6

您不能对实例进行monkeypatch__iter__(self)或扩展,next(self)因为这些方法被视为类方法,而不是作为CPython 内部优化(请参阅Special method lookup for new-style classes,了解为什么会这样)。

如果您需要对这些方法进行monkeypatch,则需要直接在类上设置它们:

class DerivedO(BaseClass):
    def __init__(self):
        self.new_count = 2
        self.__class__.next = self.__class__.new_next

    def new_next(self):
        if self.new_count:
            self.new_count -= 1
            return None
        else:
            raise StopIteration

以上将起作用;请注意,我设置__class__.next未绑定函数new_next,而不是绑定方法。

于 2012-07-27T12:44:53.860 回答
-1

由于DerivedO从不初始化count属性,因此在执行方法时会发生 AttributeError next

您可以通过安排BaseClass.__init__调用来避免此错误(显式或使用super):

class DerivedO(BaseClass):
    def __init__(self):
        super(DerivedO, self).__init__()
        self.new_count = 2

    def next(self):
        if self.new_count:
            self.new_count -= 1
            return None
        else:
            raise StopIteration

此外,new_next您可以简单地覆盖 (redefine) ,而不是定义next

于 2012-07-27T12:24:00.867 回答