Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > Conditionally implementing __iter__ in new style classes

Reply
Thread Tools

Conditionally implementing __iter__ in new style classes

 
 
Thomas Heller
Guest
Posts: n/a
 
      07-06-2005
I'm trying to implement __iter__ on an abstract base class while I don't
know whether subclasses support that or not.
Hope that makes sense, if not, this code should be clearer:

class Base:
def __getattr__(self, name):
if name == "__iter__" and hasattr(self, "Iterator"):
return self.Iterator
raise AttributeError, name

class Concrete(Base):
def Iterator(self):
yield 1
yield 2
yield 3

The idea is that if a subclass of Base defines an 'Iterator' method,
instances are iterable. They are not iterable otherwise.

The above gives the expected behaviour: iter(Base()) raises a
"TypeError: iteration over non-sequence", and iter(Concrete()) returns a
generator.

If, however, I make Base a newstyle class, this will not work any
longer. __getattr__ is never called for "__iter__" (neither is
__getattribute__, btw). Probably this has to do with data descriptors
and non-data descriptors, but I'm too tired at the moment to think
further about this.

Is there any way I could make the above code work with new style
classes?

Thanks,

Thomas

 
Reply With Quote
 
 
 
 
Thomas Heller
Guest
Posts: n/a
 
      07-06-2005
Thomas Heller <(E-Mail Removed)> writes:

> I'm trying to implement __iter__ on an abstract base class while I don't
> know whether subclasses support that or not.
> Hope that makes sense, if not, this code should be clearer:
>
> class Base:
> def __getattr__(self, name):
> if name == "__iter__" and hasattr(self, "Iterator"):
> return self.Iterator
> raise AttributeError, name
>
> class Concrete(Base):
> def Iterator(self):
> yield 1
> yield 2
> yield 3
>
> The idea is that if a subclass of Base defines an 'Iterator' method,
> instances are iterable. They are not iterable otherwise.
>
> The above gives the expected behaviour: iter(Base()) raises a
> "TypeError: iteration over non-sequence", and iter(Concrete()) returns a
> generator.
>
> If, however, I make Base a newstyle class, this will not work any
> longer. __getattr__ is never called for "__iter__" (neither is
> __getattribute__, btw). Probably this has to do with data descriptors
> and non-data descriptors, but I'm too tired at the moment to think
> further about this.
>
> Is there any way I could make the above code work with new style
> classes?


I forgot to mention this: The Base class also implements a __getitem__
method which should be used for iteration if the .Iterator method in the
subclass is not available. So it seems impossible to raise an exception
in the __iter__ method if .Iterator is not found - __iter__ MUST return
an iterator if present.

Thomas
 
Reply With Quote
 
 
 
 
infidel
Guest
Posts: n/a
 
      07-06-2005
I'm not sure I understand why you would want to. Just don't define
__iter__ on your newstyle class and you'll get the expected behavior.

 
Reply With Quote
 
infidel
Guest
Posts: n/a
 
      07-06-2005
Why not define an Iterator method in your Base class that does the
iteration using __getitem__, and any subclass that wants to do
something else just defines its own Iterator method? For that matter,
you could just use the __iter__ methods of Base and Concrete instead of
a separate method.

 
Reply With Quote
 
harold fellermann
Guest
Posts: n/a
 
      07-06-2005
> I'm trying to implement __iter__ on an abstract base class while I
> don't
> know whether subclasses support that or not.
> Hope that makes sense, if not, this code should be clearer:
>
> class Base:
> def __getattr__(self, name):
> if name == "__iter__" and hasattr(self, "Iterator"):
> return self.Iterator
> raise AttributeError, name
>
> class Concrete(Base):
> def Iterator(self):
> yield 1
> yield 2
> yield 3


I don't know how to achieve it, but why don't you simply use

class Base:
pass

class Concrete(Base):
def __iter__(self) :
yield 1
yield 2
yield 3


What is the advantage to have a baseclass that essentially does
some method renaming __iter__ ==> Iterator?

- harold -

--
Always remember that you are unique;
just like everyone else.
--

 
Reply With Quote
 
infidel
Guest
Posts: n/a
 
      07-06-2005
Something like this:

>>> class Base(object):

.... def __getitem__(self, key):
.... return key
.... def __iter__(self):
.... yield self[1]
.... yield self['foo']
.... yield self[3.0]
....
>>> class ConcreteIterable(Base):

.... def __iter__(self):
.... yield True
.... yield 'Blue'
.... yield 'Foo'
....
>>> class ConcreteNotIterable(Base):

.... pass
....
>>> [x for x in Base()]

[1, 'foo', 3.0]
>>> [x for x in ConcreteIterable()]

[True, 'Blue', 'Foo']
>>> [x for x in ConcreteNotIterable()]

[1, 'foo', 3.0]
>>>


 
Reply With Quote
 
Leif K-Brooks
Guest
Posts: n/a
 
      07-06-2005
Thomas Heller wrote:
> I forgot to mention this: The Base class also implements a __getitem__
> method which should be used for iteration if the .Iterator method in the
> subclass is not available. So it seems impossible to raise an exception
> in the __iter__ method if .Iterator is not found - __iter__ MUST return
> an iterator if present.


def Iterator(self):
for index in xrange(len(self)):
yield self[index]

def __iter__(self):
return self.Iterator()

....and then override Iterator in subclasses. But this raises the
question of why you need to use a specially-named method instead of
having subclasses override the __iter__.
 
Reply With Quote
 
Peter Otten
Guest
Posts: n/a
 
      07-06-2005
Thomas Heller wrote:

> I'm trying to implement __iter__ on an abstract base class while I don't
> know whether subclasses support that or not.
> Hope that makes sense, if not, this code should be clearer:
>
> class Base:
> def __getattr__(self, name):
> if name == "__iter__" and hasattr(self, "Iterator"):
> return self.Iterator
> raise AttributeError, name


> Is there any way I could make the above code work with new style
> classes?


Obligatory metaclass approach:

class Base:
class __metaclass__(type):
def __new__(mcl, name, bases, classdict):
try:
classdict["__iter__"] = classdict["Iterator"]
except KeyError:
pass
return type.__new__(mcl, name, bases, classdict)

class Alpha(Base):
def Iterator(self): yield 42

class Beta(Base):
def __getitem__(self, index):
return [1, 2, 3, "ganz viele"][index]


for item in Alpha(): print item
for item in Beta(): print item,
print

Peter

 
Reply With Quote
 
Bengt Richter
Guest
Posts: n/a
 
      07-06-2005
On Wed, 06 Jul 2005 17:57:42 +0200, Thomas Heller <(E-Mail Removed)> wrote:

>I'm trying to implement __iter__ on an abstract base class while I don't
>know whether subclasses support that or not.
>Hope that makes sense, if not, this code should be clearer:
>
>class Base:
> def __getattr__(self, name):
> if name == "__iter__" and hasattr(self, "Iterator"):
> return self.Iterator
> raise AttributeError, name
>
>class Concrete(Base):
> def Iterator(self):
> yield 1
> yield 2
> yield 3
>
>The idea is that if a subclass of Base defines an 'Iterator' method,
>instances are iterable. They are not iterable otherwise.
>
>The above gives the expected behaviour: iter(Base()) raises a
>"TypeError: iteration over non-sequence", and iter(Concrete()) returns a
>generator.
>
>If, however, I make Base a newstyle class, this will not work any
>longer. __getattr__ is never called for "__iter__" (neither is
>__getattribute__, btw). Probably this has to do with data descriptors
>and non-data descriptors, but I'm too tired at the moment to think
>further about this.
>
>Is there any way I could make the above code work with new style
>classes?

Will a property or custom descriptor do what you want? E.g.

>>> class Base(object):

... def __getIter(self):
... if hasattr(self, "Iterator"):
... return self.Iterator
... raise AttributeError, name
... __iter__ = property(__getIter)
...
>>> class Concrete(Base):

... def Iterator(self):
... yield 1
... yield 2
... yield 3
...
>>> iter(Base())

Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: iteration over non-sequence
>>> iter(Concrete())

<generator object at 0x02EF152C>
>>> list(iter(Concrete()))

[1, 2, 3]

Regards,
Bengt Richter
 
Reply With Quote
 
Thomas Heller
Guest
Posts: n/a
 
      07-07-2005
http://www.velocityreviews.com/forums/(E-Mail Removed) (Bengt Richter) writes:

> On Wed, 06 Jul 2005 17:57:42 +0200, Thomas Heller <(E-Mail Removed)> wrote:
>
>>I'm trying to implement __iter__ on an abstract base class while I don't
>>know whether subclasses support that or not.


> Will a property or custom descriptor do what you want? E.g.
>
> >>> class Base(object):

> ... def __getIter(self):
> ... if hasattr(self, "Iterator"):
> ... return self.Iterator
> ... raise AttributeError, name
> ... __iter__ = property(__getIter)
> ...
> >>> class Concrete(Base):

> ... def Iterator(self):
> ... yield 1
> ... yield 2
> ... yield 3
> ...
> >>> iter(Base())

> Traceback (most recent call last):
> File "<stdin>", line 1, in ?
> TypeError: iteration over non-sequence
> >>> iter(Concrete())

> <generator object at 0x02EF152C>
> >>> list(iter(Concrete()))

> [1, 2, 3]


Yep, that's exactly what I need - thanks.

Thomas
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
new.instancemethod __iter__ Martin Drautzburg Python 7 02-07-2010 03:10 PM
__iter__ yield duccio Python 6 03-11-2008 03:58 PM
string: __iter__()? mrquantum Python 10 10-04-2006 11:55 PM
why does UserDict.DictMixin use keys instead of __iter__? Steven Bethard Python 8 01-05-2005 12:58 PM
docs on for-loop with no __iter__? Steven Bethard Python 21 09-08-2004 04:23 AM



Advertisments