Reputation: 128
I'm trying to figure out why the following example won't work.
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)
And here is the output:
<__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'
As you can see the new method will not be overridden in DerivedO
when I try to assign the next()
method in __init__
. Why is that? A simple call to next will work fine, but not at all when using iterating techniques.
Edit: I realize my question wasn't completely clear. The AttributeError isn't the problem I'm looking to solve. But it does show that next()
is called on BaseClass
instead of on DerivedO
as I thought it would.
Upvotes: 4
Views: 2322
Reputation: 1121834
You cannot monkeypatch either __iter__(self)
or, by extension, next(self)
on instances because these methods are treated as class methods instead as a CPython internal optimization (see Special method lookup for new-style classes for an in-depth rationale as to why this is).
If you need to monkeypatch these methods, you'll need to set them directly on the class instead:
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
The above will work; note that I set __class__.next
to the unbound function new_next
, not to the bound method.
Upvotes: 9
Reputation: 879471
Since DerivedO
never initializes the count
attribute, an AttributeError occurs when the next
method is executed.
You could avoid this error by arranging for BaseClass.__init__
to be called (either explicitly, or by using 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
Also, instead of defining new_next
, you could simply override (redefine) next
.
Upvotes: -1