Problem with iterators and inheritance

Y

Yves

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
 
S

Scott David Daniels

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
(e-mail address removed)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,230
Latest member
LifeBoostCBD

Latest Threads

Top