471,091 Members | 1,562 Online

# Problem with iterators and inheritance

Hi!

I was fiddling around a bit with iterators and I came across some
strange behaviour. I have the following simple code:

--
class A(object):
def __init__(self, n):
self.n = n

def __iter__(self):
return self

def next(self):
if self.n > 0:
self.n -= 1
return "A: %d" % self.n
else:
raise StopIteration()

class B(A):
def __init__(self, n):
super(B,self).__init__(n)

def __iter__(self):
return self

def next(self):
if self.n > 0:
self.n -= 1
return "B: %d" % self.n
else:
raise StopIteration()

class C(A):
def __init__(self, n):
super(C,self).__init__(n)
self.next = self.mynext

def __iter__(self):
return self

def mynext(self):
if self.n > 0:
self.n -= 1
return "C: %d" % self.n
else:
raise StopIteration()
if __name__=='__main__':
a = A(2)
b = B(2)
c = C(2)
for k in [a,b,c]:
# Iterate over the object
for i in k:
print i
print "="*50

--

The output I expected was

A: 1
A: 0
==================================================
B: 1
B: 0
==================================================
C: 1
C: 0
==================================================
but strangely enough I ended up with

A: 1
A: 0
==================================================
B: 1
B: 0
==================================================
A: 1
A: 0
==================================================
Or in other words, it appears that Python does not use 'mynext' when
iterating over an object of class C. When I call c.next() directly
though, it does execute the code from the 'mynext' method. In my
understanding, iterating over a list, simply consists of repeatedly
calling the next() function until a StopIteration is raised, so there
should not really be any difference.
Another thing I noticed is that if I do not let A inherit from 'object'
(removing the calls to super(..) and adding appropriate initalization of
self.n), the result is as expected.
I am completely puzzled, so if anybody could shed some light on this,
I'd appreciate this.

YVES
May 8 '06 #1
1 1394
Yves wrote:
(in surprise because C's __init__ doesn't over-ride next)
class A(object):
def __init__(self, n): self.n = n
def __iter__(self): return self
def next(self):
if self.n > 0:
self.n -= 1
return "A: %d" % self.n
else: raise StopIteration()

class C(A):
def __init__(self, n):
super(C,self).__init__(n)
self.next = self.mynext
def __iter__(self): return self
def mynext(self):
if self.n > 0:
self.n -= 1
return "C: %d" % self.n
else:
raise StopIteration()

The answer is to understand the following code:
class Xyz(object):
def __init__(self): self.x = 23
def x(self): return 42
print Xyz().x

New-style classes control object attribute lookups,
and messages go to the class first (ignoring the instance
dictionary). That is also how "properties" work, which
(if you think about it right) could not otherwise survive
the first assignment of the property.

--Scott David Daniels
sc***********@acm.org
May 8 '06 #2

### This discussion thread is closed

Replies have been disabled for this discussion.