FAQ
Hi,

Consider the following class:
class Test(object):
... def __init__(self, obj):
... self.__obj = obj
... def __getattr__(self, name):
... return getattr(self.__obj, name)
...

Now:
a = Test([])
a.__iter__
<method-wrapper object at 0x0112CF30>
iter(a)
Traceback (most recent call last):
File "<interactive input>", line 1, in ?
TypeError: iteration over non-sequence
>>>

Is this a bug? If not, how to code Test such that iter sees the
__iter__ of the underlying object?

With my best regards,
G. Rodrigues

P.S: Tested with the latest 2.3 on win2k.

Search Discussions

  • Mark Day at Aug 6, 2003 at 9:58 pm
    In article <u8m2jvsus3om8r96ggnej9s7u3iecv3e29 at 4ax.com>, Gon?alo
    Rodrigues wrote:
    class Test(object):
    ... def __init__(self, obj):
    ... self.__obj = obj
    ... def __getattr__(self, name):
    ... return getattr(self.__obj, name)
    ...

    Now:
    a = Test([])
    a.__iter__
    <method-wrapper object at 0x0112CF30>
    iter(a)
    Traceback (most recent call last):
    File "<interactive input>", line 1, in ?
    TypeError: iteration over non-sequence
    Is this a bug? If not, how to code Test such that iter sees the
    __iter__ of the underlying object?
    I'm guessing that iter() is looking for an __iter__ attribute without
    going through __getattr__ to find it. So, I tried adding the following
    method to the Test class:

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

    but that returned the following error:

    TypeError: iter() returned non-iterator of type 'method-wrapper'

    I changed the __iter__ method to the following, and it seems to do what
    you want:

    def __iter__(self):
    return iter(self.__obj)

    As to whether this is a bug, I don't know.

    -Mark
  • Gonçalo Rodrigues at Aug 6, 2003 at 10:22 pm

    On Wed, 06 Aug 2003 14:58:02 -0700, Mark Day wrote:
    In article <u8m2jvsus3om8r96ggnej9s7u3iecv3e29 at 4ax.com>, Gon?alo
    Rodrigues wrote:
    class Test(object):
    ... def __init__(self, obj):
    ... self.__obj = obj
    ... def __getattr__(self, name):
    ... return getattr(self.__obj, name)
    ...

    Now:
    a = Test([])
    a.__iter__
    <method-wrapper object at 0x0112CF30>
    iter(a)
    Traceback (most recent call last):
    File "<interactive input>", line 1, in ?
    TypeError: iteration over non-sequence
    Is this a bug? If not, how to code Test such that iter sees the
    __iter__ of the underlying object?
    I'm guessing that iter() is looking for an __iter__ attribute without
    going through __getattr__ to find it. So, I tried adding the following
    method to the Test class:

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

    but that returned the following error:

    TypeError: iter() returned non-iterator of type 'method-wrapper'
    You forgot the parenthesis.
    I changed the __iter__ method to the following, and it seems to do what
    you want:

    def __iter__(self):
    return iter(self.__obj)
    But this is precisely what I don't want to do.

    With my best regards,
  • Terry Reedy at Aug 6, 2003 at 11:46 pm
    "Gon?alo Rodrigues" <op73418 at mail.telepac.pt> wrote in message
    news:upv2jv4eeusj7bli30j7a7ikd0cq3njvm4 at 4ax.com...
    I changed the __iter__ method to the following, and it seems to do
    what
    you want:

    def __iter__(self):
    return iter(self.__obj)
    But this is precisely what I don't want to do.
    If you don't want to do what works,
    why ask us to bother answering?

    TJR
  • Gonçalo Rodrigues at Aug 6, 2003 at 11:54 pm

    On Wed, 6 Aug 2003 19:46:39 -0400, "Terry Reedy" wrote:
    "Gon?alo Rodrigues" <op73418 at mail.telepac.pt> wrote in message
    news:upv2jv4eeusj7bli30j7a7ikd0cq3njvm4 at 4ax.com...
    I changed the __iter__ method to the following, and it seems to do
    what
    you want:

    def __iter__(self):
    return iter(self.__obj)
    But this is precisely what I don't want to do.
    If you don't want to do what works,
    why ask us to bother answering?
    Because it may not work. That is, the use case I have in mind serves
    as proxy for an object that may or may not have __iter__. IOW if I
    stick __iter__ I have to code a second class, etc... etc... Oh well...
    TJR
    With my best regards,
    G. Rodrigues
  • Gonçalo Rodrigues at Aug 7, 2003 at 10:15 am

    On Thu, 07 Aug 2003 00:54:03 +0100, Gon?alo Rodrigues wrote:
    On Wed, 6 Aug 2003 19:46:39 -0400, "Terry Reedy" wrote:


    "Gon?alo Rodrigues" <op73418 at mail.telepac.pt> wrote in message
    news:upv2jv4eeusj7bli30j7a7ikd0cq3njvm4 at 4ax.com...
    I changed the __iter__ method to the following, and it seems to do
    what
    you want:

    def __iter__(self):
    return iter(self.__obj)
    But this is precisely what I don't want to do.
    If you don't want to do what works,
    why ask us to bother answering?
    Because it may not work. That is, the use case I have in mind serves
    as proxy for an object that may or may not have __iter__. IOW if I
    stick __iter__ I have to code a second class, etc... etc... Oh well...
    Replying to myself:

    Just to make things clearer, having iter(self.__obj) as in

    def __iter__(self):
    return iter(self.__obj)

    Would mean that my proxy class would become an iterable. And that is
    not what I want because *other* parts in my code check that an object
    is in iterable by looking for __iter__. Suffice it to say that fake
    iterables, although not the worst thing in the world, are not
    desirable. Maybe this means that I have to approach things in another
    way. Humpf.

    With my best regards,
    G. Rodrigues
  • Terry Reedy at Aug 7, 2003 at 4:39 pm
    "Gon?alo Rodrigues" <op73418 at mail.telepac.pt> wrote in message
    news:md94jvccu02b9dv5890k34629rkot79roj at 4ax.com...
    On Thu, 07 Aug 2003 00:54:03 +0100, Gon?alo Rodrigues
    wrote:
    On Wed, 6 Aug 2003 19:46:39 -0400, "Terry Reedy" <tjreedy at udel.edu>
    If you don't want to do what works,
    why ask us to bother answering?
    Because it may not work. That is, the use case I have in mind
    serves
    as proxy for an object that may or may not have __iter__. IOW if I
    stick __iter__ I have to code a second class, etc... etc... Oh
    well...
    Just to make things clearer, having iter(self.__obj) as in

    def __iter__(self):
    return iter(self.__obj)

    Would mean that my proxy class would become an iterable. And that is
    not what I want because *other* parts in my code check that an object
    is in iterable by looking for __iter__.
    As I understand your clarification, you want the proxy to actually be
    iterable (via delegation) when given as an arg to iter(), but to not
    look iterable to other code (call it test_iterable()). And your
    test_iterable() *currently* looks only for the presence/absence of the
    very thing needed to make iter() work.

    It is definitely not a Python bug that this does not work.

    But I am still not sure of your spec for test_iterable(). If you want
    it to always return false for the proxies, it needs another indicator
    that iter() doesn't use (which is what you were trying to do with your
    __getattr__). So add the following line to your proxy class:

    not_iterable = True # or 1 if pre2.2

    and have test_iterable check this next if it does find __iter__.

    def test_iterable(obj): #untested
    return hasattr(obj, '__iter__') and not hasattr(obj,
    'not_iterable')

    If you want it to return true or false (for proxies) according to the
    wrapped object, then it must directly or indirectly test the wrapped
    object.

    Terry J. Reedy
  • Gonçalo Rodrigues at Aug 7, 2003 at 5:38 pm

    On Thu, 7 Aug 2003 12:39:06 -0400, "Terry Reedy" wrote:
    "Gon?alo Rodrigues" <op73418 at mail.telepac.pt> wrote in message
    news:md94jvccu02b9dv5890k34629rkot79roj at 4ax.com...
    On Thu, 07 Aug 2003 00:54:03 +0100, Gon?alo Rodrigues
    wrote:
    On Wed, 6 Aug 2003 19:46:39 -0400, "Terry Reedy" <tjreedy at udel.edu>
    If you don't want to do what works,
    why ask us to bother answering?
    Because it may not work. That is, the use case I have in mind
    serves
    as proxy for an object that may or may not have __iter__. IOW if I
    stick __iter__ I have to code a second class, etc... etc... Oh
    well...
    Just to make things clearer, having iter(self.__obj) as in

    def __iter__(self):
    return iter(self.__obj)

    Would mean that my proxy class would become an iterable. And that is
    not what I want because *other* parts in my code check that an object
    is in iterable by looking for __iter__.
    As I understand your clarification, you want the proxy to actually be
    iterable (via delegation) when given as an arg to iter(), but to not
    look iterable to other code (call it test_iterable()). And your
    test_iterable() *currently* looks only for the presence/absence of the
    very thing needed to make iter() work.
    To look an iterable by delegation *if* the underlying object is, yes.
    It is definitely not a Python bug that this does not work.
    It finally dawned on me why this is so: it's the way new classes work.
    In a sloppy language: a __getattr__ hook is like an instance
    attribute, but the iter machinery goes after the class not the
    instance so it misses __getattr__.

    So my test_iterable (in your nomenclature) is wrongly coded because it
    gives false positives.

    [Ideas snipped]

    Thanks for the input. But since something has to change anyway, the
    best idea seems to be to stick __iter__ in the proxy class after all
    and change the other code that was counting on testing iterableness by
    looking __iter__. This will involve some changes, but it's the "least
    worse of all evils."

    With my best regards,
    G. Rodrigues
  • Michele Simionato at Aug 8, 2003 at 11:42 am
    Gon?alo Rodrigues <op73418 at mail.telepac.pt> wrote in message news:<qp25jv04oct9v44vudgi5lt4t1ukqtnjt9 at 4ax.com>...
    It finally dawned on me why this is so: it's the way new classes work.
    In a sloppy language: a __getattr__ hook is like an instance
    attribute, but the iter machinery goes after the class not the
    instance so it misses __getattr__.
    No, iter does not work even if you define __getattr__ at the metaclass
    level, i.e. iter(c) is not type(c).__iter__(c) if "__iter__" is defined
    via __getattr__ :
    class M(type):
    def __getattr__(cls,name):
    if name=='__iter__': return lambda self:iter([])
    class C: __metaclass__=M
    c=C()
    iter(c)
    Traceback (most recent call last):
    File "<pyshell#10>", line 1, in -toplevel-
    iter(c)
    TypeError: iteration over non-sequence
    C.__iter__(c)
    <listiterator object at 0x00A3DB30>

    having-brought-more-confusion-in-your-head-yours-sincerely,

    Michele
  • Steven Taschuk at Aug 7, 2003 at 1:43 am
    Quoth Mark Day:
    [...]
    I'm guessing that iter() is looking for an __iter__ attribute without
    going through __getattr__ to find it. [...]
    Right. This is normal for special methods when invoked by special
    notations (e.g., __len__ invoked by way of len(), __add__ invoked
    by way of +, etc.).
    [...] So, I tried adding the following
    method to the Test class:

    def __iter__(self):
    return self.__obj.__iter__
    Well, you have to actually call __iter__ to get the iterator:

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

    (This makes iter(testobj) not quite the same as
    iter(testobj.__obj) for new-style instances, mind you.)

    --
    Steven Taschuk staschuk at telusplanet.net
    "Telekinesis would be worth patenting." -- James Gleick
  • Aahz at Aug 6, 2003 at 11:35 pm
    In article <u8m2jvsus3om8r96ggnej9s7u3iecv3e29 at 4ax.com>,
    Gon?alo Rodrigues wrote:
    class Test(object):
    ... def __init__(self, obj):
    ... self.__obj = obj
    ... def __getattr__(self, name):
    ... return getattr(self.__obj, name)
    ...
    a = Test([])
    a.__iter__
    <method-wrapper object at 0x0112CF30>
    iter(a)
    Traceback (most recent call last):
    File "<interactive input>", line 1, in ?
    TypeError: iteration over non-sequence
    Is this a bug? If not, how to code Test such that iter sees the
    __iter__ of the underlying object?
    As Mark guessed, iter() goes directly to the attribute rather than using
    the __getattr__ machinery of the class. However, you can intercept it
    using a metaclass, but that requires a bit of fancy footwork to reach
    down into the instance to get self.__obj.
    --
    Aahz (aahz at pythoncraft.com) <*> http://www.pythoncraft.com/

    This is Python. We don't care much about theory, except where it intersects
    with useful practice. --Aahz
  • Gonçalo Rodrigues at Aug 6, 2003 at 11:57 pm

    On 6 Aug 2003 19:35:18 -0400, aahz at pythoncraft.com (Aahz) wrote:
    In article <u8m2jvsus3om8r96ggnej9s7u3iecv3e29 at 4ax.com>,
    Gon?alo Rodrigues wrote:
    class Test(object):
    ... def __init__(self, obj):
    ... self.__obj = obj
    ... def __getattr__(self, name):
    ... return getattr(self.__obj, name)
    ...
    a = Test([])
    a.__iter__
    <method-wrapper object at 0x0112CF30>
    iter(a)
    Traceback (most recent call last):
    File "<interactive input>", line 1, in ?
    TypeError: iteration over non-sequence
    Is this a bug? If not, how to code Test such that iter sees the
    __iter__ of the underlying object?
    As Mark guessed, iter() goes directly to the attribute rather than using
    the __getattr__ machinery of the class. However, you can intercept it
    using a metaclass, but that requires a bit of fancy footwork to reach
    down into the instance to get self.__obj.
    Actually I came across this because of a little metaclass I was
    coding. And my head hurts already as it is...

    So, is it a bug? In other words, should I open a bug report?

    With my best regards,
    G. Rodrigues
  • Aahz at Aug 7, 2003 at 2:57 pm
    In article <ca53jvok499s0ufs34sjvgsudkpqqamupl at 4ax.com>,
    Gon?alo Rodrigues wrote:
    So, is it a bug? In other words, should I open a bug report?
    I'd say it's not a bug. It's a natural consequence of the way new-style
    classes are intended to work. OTOH, opening a bug report may result in
    improvement of the documentation -- if you do choose a bug report, I'd
    suggest taking that tack.
    --
    Aahz (aahz at pythoncraft.com) <*> http://www.pythoncraft.com/

    This is Python. We don't care much about theory, except where it intersects
    with useful practice. --Aahz
  • Michele Simionato at Aug 7, 2003 at 5:19 pm

    Gon?alo Rodrigues <op73418 at mail.telepac.pt> wrote in message news:
    So, is it a bug? In other words, should I open a bug report?

    With my best regards,
    G. Rodrigues
    I would open a bug report; at the best this is a documentation bug,
    since
    the fact that ___getattr__ is skipped by special methods is never
    mentioned in the documentation, at least AFAIK. Something similar with
    __len__ caused confusion at least twice in the last few monts.

    See for instance

    http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&frame=right&thÈ09d56daee70926&seekm"59b0e2.0307040511.1eb91faa%40posting.google.com#link6

    Michele
  • Alex Martelli at Aug 7, 2003 at 5:28 pm

    Michele Simionato wrote:

    Gon?alo Rodrigues <op73418 at mail.telepac.pt> wrote in message news:
    So, is it a bug? In other words, should I open a bug report?

    With my best regards,
    G. Rodrigues
    I would open a bug report; at the best this is a documentation bug,
    I disagree that it's a documentation bug.
    since
    the fact that ___getattr__ is skipped by special methods is never
    The language reference says:
    """ A class can implement certain operations ... by defining methods
    with special names. """

    This is VERY explicit -- "__getattr__ is skipped" is false (the
    __getattr__ a *metaclass* could define would indeed be used as
    equivalent to the class defining methods) -- the _class_'s __getattr__
    if any is irrelevant because it's used on class *instances* (as
    the language reference also says a few pages later) and thus it's
    not the *CLASS* that's defining "methods with special names" here.

    If anything, a mention might be warranted that, for compatibility,
    a bug is retained in old-style classes, whereby, in addition to the
    class's ability to define methods with special names, any old-style
    class INSTANCE may also define them (e.g. through __getattr__) in
    contrast with what the docs have just said about the general case.

    I do suspect that the peculiarities of old-style classes are not
    well documented today, more generally than just this tidbit, alas.


    Alex
  • Michele Simionato at Aug 7, 2003 at 10:54 pm
    Alex Martelli <aleax at aleax.it> wrote in message news:<t%vYa.29160$an6.1020776 at news1.tin.it>...
    "__getattr__ is skipped" is false (the
    __getattr__ a *metaclass* could define would indeed be used as
    equivalent to the class defining methods) -- the _class_'s __getattr__
    if any is irrelevant because it's used on class *instances* (as
    the language reference also says a few pages later) and thus it's
    not the *CLASS* that's defining "methods with special names" here.
    Alex, did you look at the thread I mentioned? Here is the problem
    with __getattr__ in metaclasses for special methods (quoting from
    that thread).

    """
    defining __len__ on the class does not work:

    class M(type):
    def __getattr__(self,name):
    if name=='__len__': return lambda self:0

    class F: __metaclass__=M
    f=F()
    F.__len__(f) # okay 0
    len(f) # TypeError: len() of unsized object
    f.__len__() # AttributeError: 'F' object has no attribute '__len__'

    As you see, the problem is that len(x) is not calling x.__len__(),
    nor x.__class__.__len__(x); I really would like to know how ``len``
    (or ``str``, ``repr``, etc.) work.
    """

    If you remember, Bjorn Pettersen reported this (or something similar)
    months ago. I am saying that this behaviour is not documented, at
    least AFAIK.


    Michele
  • Bengt Richter at Aug 8, 2003 at 2:19 am

    On 7 Aug 2003 15:54:33 -0700, mis6 at pitt.edu (Michele Simionato) wrote:
    Alex Martelli <aleax at aleax.it> wrote in message news:<t%vYa.29160$an6.1020776 at news1.tin.it>...
    "__getattr__ is skipped" is false (the
    __getattr__ a *metaclass* could define would indeed be used as
    equivalent to the class defining methods) -- the _class_'s __getattr__
    if any is irrelevant because it's used on class *instances* (as
    the language reference also says a few pages later) and thus it's
    not the *CLASS* that's defining "methods with special names" here.
    Alex, did you look at the thread I mentioned? Here is the problem
    with __getattr__ in metaclasses for special methods (quoting from
    that thread).

    """
    defining __len__ on the class does not work:

    class M(type):
    def __getattr__(self,name):
    if name=='__len__': return lambda self:0

    class F: __metaclass__=M
    f=F()
    F.__len__(f) # okay 0
    len(f) # TypeError: len() of unsized object
    f.__len__() # AttributeError: 'F' object has no attribute '__len__'

    As you see, the problem is that len(x) is not calling x.__len__(),
    nor x.__class__.__len__(x); I really would like to know how ``len``
    (or ``str``, ``repr``, etc.) work.
    """
    Not very tested, but maybe you have to write it something like this:
    class M(type):
    ... def __new__(cls, name, bases, cdict):
    ... cdict['__len__'] = lambda self:0
    ... return type.__new__(cls, name, bases, cdict)
    ...
    class F: __metaclass__ = M
    ...
    f=F()
    F.__len__
    <unbound method F.<lambda>>
    F.__len__(f)
    len(f)
    hasattr(M,'__len__')
    False
    hasattr(F,'__len__')
    True

    Or am I missing the point? ;-/
    If you remember, Bjorn Pettersen reported this (or something similar)
    months ago. I am saying that this behaviour is not documented, at
    least AFAIK.
    Regards,
    Bengt Richter
  • Michele Simionato at Aug 8, 2003 at 11:23 am
    bokr at oz.net (Bengt Richter) wrote in message news:<bgv1bf$eoi$0 at 216.39.172.122>...
    Not very tested, but maybe you have to write it something like this:
    class M(type):
    ... def __new__(cls, name, bases, cdict):
    ... cdict['__len__'] = lambda self:0
    ... return type.__new__(cls, name, bases, cdict)
    ...
    class F: __metaclass__ = M
    ...
    f=F()
    F.__len__
    <unbound method F.<lambda>>
    F.__len__(f)
    len(f)
    hasattr(M,'__len__')
    False
    hasattr(F,'__len__')
    True

    Or am I missing the point? ;-/

    Regards,
    Bengt Richter
    Yes, you are missing the point, indeed ;)

    The point is that if you define a fake special method via __getattr__, it
    works if it is called as F.__len___(f), but it DOES not work if it is
    called as len(f). I suspect Gonzalo's problem is the same with iter.

    Built-in like iter, len, etc. look directly for special methods __iter__,
    __len__ etc., *without* checking if they are defined by __getattr__. I
    am not saying that this is necessarely a bug, I am saying that this is
    not documented and that at least three persons have been beaten by this
    issue in the last few months. Not me personally, hence the reason why I didn't
    submit a bug report, but this time (if Gonzalo is not going to do that), I
    will submit the report, unless Alex is going to prove that this is
    already documented ;)

    Michele
  • Alex Martelli at Aug 8, 2003 at 12:08 pm
    Michele Simionato wrote:
    ...
    The point is that if you define a fake special method via __getattr__, it
    works if it is called as F.__len___(f), but it DOES not work if it is
    called as len(f). I suspect Gonzalo's problem is the same with iter.
    You're right. Special methods are looked up in the 'slots' (tp_iter and
    the like), for the purpose of Python operations and built-ins (iter() and
    the like), and __getattr__, being dynamic, doesn't affect those slots.

    E.g.:

    def fakeiter(x):
    return iter('ciao')

    class witer(type):
    def __getattr__(cls, name):
    if name == '__iter__':
    cls.__iter__ = fakeiter
    return cls.__iter__
    raise AttributeError, name

    __metaclass__ = witer

    class X: pass

    try:
    for x in iter(X()):
    print x,
    print
    except TypeError:
    print 'TypeError, as expected'
    else:
    print "No TypeError (?!)"

    try:
    for x in X().__iter__(): print x,
    print
    except AttributeError:
    print 'AttributeError, as expected'
    else:
    print "No AttributeError (?!)"

    for x in X.__iter__(X()): print x,
    print

    try:
    for x in iter(X()):
    print x,
    print
    except TypeError:
    print 'TypeError, alas'
    else:
    print "No TypeError (good!)"

    Once X.__iter__ IS finally accessed, it gets installed in the
    class object, thereby affecting the slots and making iter happy.

    But until that happens, the class's tp_iter is null, iter()
    does not cause a __getattr__ call, and X().__iter__, being an
    access on the instance, doesn't go up to the metaclass either.

    Built-in like iter, len, etc. look directly for special methods __iter__,
    __len__ etc., *without* checking if they are defined by __getattr__. I
    am not saying that this is necessarely a bug, I am saying that this is
    not documented and that at least three persons have been beaten by this
    I agree. The way the Nutshell puts it is "in the new-style object
    model, implicit use of special methods always relies on the class-level
    binding of the special method, if any" -- and this is correct and
    complete, even though probably too terse for anything except perhaps
    a Language Reference (which IS allowed to be very terse as long as
    it's correct and complete). __getattr__ is not a BINDING of the
    special method, though it may be considered a DEFINITION of it, which
    is why the current phrase in the Language Reference is not 100% correct
    and complete -- only 99.44%, and I agree that the remaining 0.56%
    _is_ a delicate defect in the documentation.

    It would probably be more helpful to most readers if a section on
    new-style classes, one on old-style classes, and/or one on special
    methods, expanded on this issue just a wee little bit. Alas, the
    Language Reference being meant for language-lawyers only, it's hard
    to argue that it should deign to clarify things, as long as they
    _are_ expressed in ways that are correct and complete.
    issue in the last few months. Not me personally, hence the reason why I
    didn't submit a bug report, but this time (if Gonzalo is not going to do
    that), I will submit the report, unless Alex is going to prove that this
    is already documented ;)
    It's not _correctly and completely_ documented, I have to agree -- the
    docs use the potentialy-slightly-ambiguous verb 'define' rather than the
    more specific one 'bind'.


    Alex
  • Gonçalo Rodrigues at Aug 8, 2003 at 1:57 pm
    On 8 Aug 2003 04:23:48 -0700, mis6 at pitt.edu (Michele Simionato) wrote:

    [text snipped]
    Not me personally, hence the reason why I didn't
    submit a bug report, but this time (if Gonzalo is not going to do that), I
    will submit the report, unless Alex is going to prove that this is
    already documented ;)
    I'm gonna do it this weekend. I'll post a kind of abstract of what I
    learned from this thread.

    With my best regards,
    G. Rodrigues
  • Bengt Richter at Aug 8, 2003 at 12:33 am

    On Thu, 07 Aug 2003 00:57:30 +0100, Gon?alo Rodrigues wrote:
    On 6 Aug 2003 19:35:18 -0400, aahz at pythoncraft.com (Aahz) wrote:

    In article <u8m2jvsus3om8r96ggnej9s7u3iecv3e29 at 4ax.com>,
    Gon?alo Rodrigues wrote:
    class Test(object):
    ... def __init__(self, obj):
    ... self.__obj = obj
    ... def __getattr__(self, name):
    ... return getattr(self.__obj, name)
    ...
    a = Test([])
    a.__iter__
    <method-wrapper object at 0x0112CF30>
    iter(a)
    Traceback (most recent call last):
    File "<interactive input>", line 1, in ?
    TypeError: iteration over non-sequence
    Is this a bug? If not, how to code Test such that iter sees the
    __iter__ of the underlying object?
    As Mark guessed, iter() goes directly to the attribute rather than using
    the __getattr__ machinery of the class. However, you can intercept it
    using a metaclass, but that requires a bit of fancy footwork to reach
    down into the instance to get self.__obj.
    Actually I came across this because of a little metaclass I was
    coding. And my head hurts already as it is...

    So, is it a bug? In other words, should I open a bug report?
    I don't think it's a bug, but there's a lot to explore ;-)
    Here is something I've been hacking at to do some exploring. Maybe you will find it interesting:

    ====< for_grodrigues.py >=============================================
    class Test(object):
    def __init__(self, *args, **kw): pass
    def __new__(cls, obj):
    # helper to capture method in closure for method delegator
    def mk_meth_deleg(obj, meth, name):
    def meth_deleg(arg0, *args, **kw):
    if isinstance(arg0, type):
    return meth(arg0, *args, **kw)
    else:
    return meth(obj, *args, **kw)
    return meth_deleg

    # get the objects's class dict to build a new class
    proxy_cdict = obj.__class__.__dict__.copy()
    for name, meth in proxy_cdict.items():
    if( name.startswith('__') and
    callable(meth) and
    #not name.startswith('__getattr') and
    not name == '__init__'
    ):
    m = mk_meth_deleg(obj, meth, name)
    proxy_cdict[name] = m

    # customize proxy class as desired here
    def __getitem__(self, i):
    if i==2: return '[Test-modified %r]'%obj[i]
    else: return obj[i]
    proxy_cdict['__getitem__'] = __getitem__

    #derive from object's class if possible for stuff we haven't caught??
    try:
    type('',(obj.__class__,), {})
    except TypeError:
    proxy_base = object
    else:
    proxy_base = obj.__class__
    proxy_class = type(
    obj.__class__.__name__+'_proxy',
    (proxy_base,),
    proxy_cdict)
    proxy_obj = proxy_class()
    proxy_obj.instattr = 'proxy_obj instance attribute set by Test'
    return proxy_obj
    ======================================================================
    Sample interaction:
    from for_grodrigues import Test
    a = Test([1,2,3])
    a
    [1, 2, 3]
    type(a)
    <class 'for_grodrigues.list_proxy'>
    a.__iter__
    <method-wrapper object at 0x00902990>
    iter(a)
    <listiterator object at 0x00902970>
    a.__iter__()
    <listiterator object at 0x00902910>
    ait = iter(a)
    ait.next()
    1
    for i in a: print i,
    ...
    1 2 3

    Note that the __iter__ bypassed the doctored __getitem__ of our proxy object, vs:
    for i in range(3): print a[i],
    ...
    1 2 [Test-modified 3]

    b = Test(None)
    type(b)
    <class 'for_grodrigues.NoneType_proxy'>
    b
    None
    b[2]
    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    File "for_grodrigues.py", line 26, in __getitem__
    if i==2: return '[Test-modified %r]'%obj[i]
    TypeError: unsubscriptable object

    Now we try to iterate over the string, but it doesn't provide __iter__ apparently,
    since we can see that it wound up using __getitem__:
    c = Test('abcd')
    for x in c: print x,
    ...
    a b [Test-modified 'c'] d

    vs, again the list:
    a
    [1, 2, 3]
    type(a)
    <class 'for_grodrigues.list_proxy'>
    for i in a: print i,
    ...
    1 2 3
    c
    'abcd'
    c[::-1]
    'dcba'
    That doesn't use __getitem__ either.

    By inheriting from the object's class, we pick up base class methods:
    class A(object):
    ... def afoo(self): return 'afoo()'
    ...
    class B(A):
    ... def bfoo(self): return 'bfoo()'
    ...
    b = B()
    pb = Test(b)
    pb.bfoo()
    'bfoo()'
    pb.afoo()
    'afoo()'
    type(pb)
    <class '__main__.B_proxy'>

    There are interesting issues of proxy object attribute space vs the real object vs the
    proxy class methods vs the real object's methods, vs inheritance for it, etc.
    and you could alter the priorities.

    You can mess with it yourself, and maybe eliminate some hacking cruft ;-)

    Regards,
    Bengt Richter
  • Bengt Richter at Aug 7, 2003 at 8:46 am

    On Wed, 06 Aug 2003 20:40:25 +0100, Gon?alo Rodrigues wrote:
    Hi,

    Consider the following class:
    class Test(object):
    ... def __init__(self, obj):
    ... self.__obj = obj
    ... def __getattr__(self, name):
    ... return getattr(self.__obj, name)
    ...

    Now:
    a = Test([])
    a.__iter__
    <method-wrapper object at 0x0112CF30>
    iter(a)
    Traceback (most recent call last):
    File "<interactive input>", line 1, in ?
    TypeError: iteration over non-sequence
    Is this a bug? If not, how to code Test such that iter sees the
    __iter__ of the underlying object?
    Perhaps this way?
    class Test(object):
    ... def __init__(self, obj):
    ... self.__obj = obj
    ... def __getattribute__(self, name):
    ... return getattr(object.__getattribute__(self, '_Test__obj'), name)
    ... def __iter__(self):
    ... return iter(object.__getattribute__(self, '_Test__obj'))
    ...
    a = Test([11, 22])
    a.__iter__
    <method-wrapper object at 0x00902DB0>
    iter(a)
    <listiterator object at 0x00902C50>
    for i in a: print i,
    ...
    11 22
    b = Test('abc')
    for c in b: print `c`,
    ...
    'a' 'b' 'c'
    c = Test(123)
    for x in c: print x
    ...
    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    File "<stdin>", line 7, in __iter__
    TypeError: iteration over non-sequence
    >>>

    Regards,
    Bengt Richter

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
grouppython-list @
categoriespython
postedAug 6, '03 at 7:40p
activeAug 8, '03 at 1:57p
posts22
users8
websitepython.org

People

Translate

site design / logo © 2022 Grokbase