Bug? If not, how to work around it?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Gonçalo Rodrigues

    Bug? If not, how to work around it?

    Hi,

    Consider the following class:
    [color=blue][color=green][color=darkred]
    >>> class Test(object):[/color][/color][/color]
    .... def __init__(self, obj):
    .... self.__obj = obj
    .... def __getattr__(sel f, name):
    .... return getattr(self.__ obj, name)
    ....

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

    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.
  • Mark Day

    #2
    Re: Bug? If not, how to work around it?

    In article <u8m2jvsus3om8r 96ggnej9s7u3iec v3e29@4ax.com>, Gonçalo
    Rodrigues <op73418@mail.t elepac.pt> wrote:
    [color=blue][color=green][color=darkred]
    > >>> class Test(object):[/color][/color]
    > ... def __init__(self, obj):
    > ... self.__obj = obj
    > ... def __getattr__(sel f, name):
    > ... return getattr(self.__ obj, name)
    > ...
    >
    > Now:
    >[color=green][color=darkred]
    > >>> a = Test([])
    > >>> a.__iter__[/color][/color]
    > <method-wrapper object at 0x0112CF30>[color=green][color=darkred]
    > >>> iter(a)[/color][/color]
    > Traceback (most recent call last):
    > File "<interacti ve input>", line 1, in ?
    > TypeError: iteration over non-sequence[color=green][color=darkred]
    > >>>[/color][/color]
    >
    > Is this a bug? If not, how to code Test such that iter sees the
    > __iter__ of the underlying object?[/color]

    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.__it er__

    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

    Comment

    • Aahz

      #3
      Re: Bug? If not, how to work around it?

      In article <u8m2jvsus3om8r 96ggnej9s7u3iec v3e29@4ax.com>,
      Gonçalo Rodrigues <op73418@mail.t elepac.pt> wrote:[color=blue]
      >[color=green][color=darkred]
      >>>> class Test(object):[/color][/color]
      >... def __init__(self, obj):
      >... self.__obj = obj
      >... def __getattr__(sel f, name):
      >... return getattr(self.__ obj, name)
      >...[color=green][color=darkred]
      >>>> a = Test([])
      >>>> a.__iter__[/color][/color]
      ><method-wrapper object at 0x0112CF30>[color=green][color=darkred]
      >>>> iter(a)[/color][/color]
      >Traceback (most recent call last):
      > File "<interacti ve input>", line 1, in ?
      >TypeError: iteration over non-sequence[color=green][color=darkred]
      >>>>[/color][/color]
      >
      >Is this a bug? If not, how to code Test such that iter sees the
      >__iter__ of the underlying object?[/color]

      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@pythoncra ft.com) <*> http://www.pythoncraft.com/

      This is Python. We don't care much about theory, except where it intersects
      with useful practice. --Aahz

      Comment

      • Gonçalo Rodrigues

        #4
        Re: Bug? If not, how to work around it?

        On Wed, 6 Aug 2003 19:46:39 -0400, "Terry Reedy" <tjreedy@udel.e du>
        wrote:
        [color=blue]
        >
        >"Gonçalo Rodrigues" <op73418@mail.t elepac.pt> wrote in message
        >news:upv2jv4ee usj7bli30j7a7ik d0cq3njvm4@4ax. com...[color=green][color=darkred]
        >> >I changed the __iter__ method to the following, and it seems to do[/color][/color]
        >what[color=green][color=darkred]
        >> >you want:
        >> >
        >> > def __iter__(self):
        >> > return iter(self.__obj )[/color][/color]
        >[color=green]
        >> But this is precisely what I don't want to do.[/color]
        >
        >If you don't want to do what works,
        >why ask us to bother answering?
        >[/color]

        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...
        [color=blue]
        >TJR
        >[/color]

        With my best regards,
        G. Rodrigues

        Comment

        • Gonçalo Rodrigues

          #5
          Re: Bug? If not, how to work around it?

          On 6 Aug 2003 19:35:18 -0400, aahz@pythoncraf t.com (Aahz) wrote:
          [color=blue]
          >In article <u8m2jvsus3om8r 96ggnej9s7u3iec v3e29@4ax.com>,
          >Gonçalo Rodrigues <op73418@mail.t elepac.pt> wrote:[color=green]
          >>[color=darkred]
          >>>>> class Test(object):[/color]
          >>... def __init__(self, obj):
          >>... self.__obj = obj
          >>... def __getattr__(sel f, name):
          >>... return getattr(self.__ obj, name)
          >>...[color=darkred]
          >>>>> a = Test([])
          >>>>> a.__iter__[/color]
          >><method-wrapper object at 0x0112CF30>[color=darkred]
          >>>>> iter(a)[/color]
          >>Traceback (most recent call last):
          >> File "<interacti ve input>", line 1, in ?
          >>TypeError: iteration over non-sequence[color=darkred]
          >>>>>[/color]
          >>
          >>Is this a bug? If not, how to code Test such that iter sees the
          >>__iter__ of the underlying object?[/color]
          >
          >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.[/color]

          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

          Comment

          • Steven Taschuk

            #6
            Re: Bug? If not, how to work around it?

            Quoth Mark Day:
            [...][color=blue]
            > I'm guessing that iter() is looking for an __iter__ attribute without
            > going through __getattr__ to find it. [...][/color]

            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.).
            [color=blue]
            > [...] So, I tried adding the following
            > method to the Test class:
            >
            > def __iter__(self):
            > return self.__obj.__it er__[/color]

            Well, you have to actually call __iter__ to get the iterator:

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

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

            --
            Steven Taschuk staschuk@telusp lanet.net
            "Telekinesi s would be worth patenting." -- James Gleick

            Comment

            • Gonçalo Rodrigues

              #7
              Re: Bug? If not, how to work around it?

              On Thu, 07 Aug 2003 00:54:03 +0100, Gonçalo Rodrigues
              <op73418@mail.t elepac.pt> wrote:
              [color=blue]
              >On Wed, 6 Aug 2003 19:46:39 -0400, "Terry Reedy" <tjreedy@udel.e du>
              >wrote:
              >[color=green]
              >>
              >>"Gonçalo Rodrigues" <op73418@mail.t elepac.pt> wrote in message
              >>news:upv2jv4e eusj7bli30j7a7i kd0cq3njvm4@4ax .com...[color=darkred]
              >>> >I changed the __iter__ method to the following, and it seems to do[/color]
              >>what[color=darkred]
              >>> >you want:
              >>> >
              >>> > def __iter__(self):
              >>> > return iter(self.__obj )[/color]
              >>[color=darkred]
              >>> But this is precisely what I don't want to do.[/color]
              >>
              >>If you don't want to do what works,
              >>why ask us to bother answering?
              >>[/color]
              >
              >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...
              >[/color]

              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

              Comment

              • Aahz

                #8
                Re: Bug? If not, how to work around it?

                In article <ca53jvok499s0u fs34sjvgsudkpqq amupl@4ax.com>,
                Gonçalo Rodrigues <op73418@mail.t elepac.pt> wrote:[color=blue]
                >
                >So, is it a bug? In other words, should I open a bug report?[/color]

                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@pythoncra ft.com) <*> http://www.pythoncraft.com/

                This is Python. We don't care much about theory, except where it intersects
                with useful practice. --Aahz

                Comment

                • Alex Martelli

                  #9
                  Re: Bug? If not, how to work around it?

                  Michele Simionato wrote:
                  [color=blue]
                  > Gonçalo Rodrigues <op73418@mail.t elepac.pt> wrote in message news:[color=green]
                  >> So, is it a bug? In other words, should I open a bug report?
                  >>
                  >> With my best regards,
                  >> G. Rodrigues[/color]
                  >
                  > I would open a bug report; at the best this is a documentation bug,[/color]

                  I disagree that it's a documentation bug.
                  [color=blue]
                  > since
                  > the fact that ___getattr__ is skipped by special methods is never[/color]

                  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

                  Comment

                  • Gonçalo Rodrigues

                    #10
                    Re: Bug? If not, how to work around it?

                    On Thu, 7 Aug 2003 12:39:06 -0400, "Terry Reedy" <tjreedy@udel.e du>
                    wrote:
                    [color=blue]
                    >
                    >"Gonçalo Rodrigues" <op73418@mail.t elepac.pt> wrote in message
                    >news:md94jvccu 02b9dv5890k3462 9rkot79roj@4ax. com...[color=green]
                    >> On Thu, 07 Aug 2003 00:54:03 +0100, Gonçalo Rodrigues
                    >> <op73418@mail.t elepac.pt> wrote:
                    >>[color=darkred]
                    >> >On Wed, 6 Aug 2003 19:46:39 -0400, "Terry Reedy" <tjreedy@udel.e du>
                    >> >>If you don't want to do what works,
                    >> >>why ask us to bother answering?[/color][/color]
                    >[color=green][color=darkred]
                    >> >Because it may not work. That is, the use case I have in mind[/color][/color]
                    >serves[color=green][color=darkred]
                    >> >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[/color][/color]
                    >well...
                    >[color=green]
                    >> 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[/color]
                    >object[color=green]
                    >> is in iterable by looking for __iter__.[/color]
                    >
                    >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.
                    >[/color]

                    To look an iterable by delegation *if* the underlying object is, yes.
                    [color=blue]
                    >It is definitely not a Python bug that this does not work.
                    >[/color]

                    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

                    Comment

                    • Michele Simionato

                      #11
                      Re: Bug? If not, how to work around it?

                      Alex Martelli <aleax@aleax.it > wrote in message news:<t%vYa.291 60$an6.1020776@ news1.tin.it>.. .[color=blue]
                      > "__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.
                      >[/color]

                      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__(sel f,name):
                      if name=='__len__' : return lambda self:0

                      class F: __metaclass__=M
                      [color=blue][color=green][color=darkred]
                      >>> f=F()
                      >>> F.__len__(f) # okay 0
                      >>> len(f) # TypeError: len() of unsized object[/color][/color][/color]
                      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__.__l en__(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

                      Comment

                      • Bengt Richter

                        #12
                        Re: Bug? If not, how to work around it?

                        On Thu, 07 Aug 2003 00:57:30 +0100, Gonçalo Rodrigues <op73418@mail.t elepac.pt> wrote:
                        [color=blue]
                        >On 6 Aug 2003 19:35:18 -0400, aahz@pythoncraf t.com (Aahz) wrote:
                        >[color=green]
                        >>In article <u8m2jvsus3om8r 96ggnej9s7u3iec v3e29@4ax.com>,
                        >>Gonçalo Rodrigues <op73418@mail.t elepac.pt> wrote:[color=darkred]
                        >>>
                        >>>>>> class Test(object):
                        >>>... def __init__(self, obj):
                        >>>... self.__obj = obj
                        >>>... def __getattr__(sel f, name):
                        >>>... return getattr(self.__ obj, name)
                        >>>...
                        >>>>>> a = Test([])
                        >>>>>> a.__iter__
                        >>><method-wrapper object at 0x0112CF30>
                        >>>>>> iter(a)
                        >>>Traceback (most recent call last):
                        >>> File "<interacti ve 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?[/color]
                        >>
                        >>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.[/color]
                        >
                        >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?
                        >[/color]
                        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(o bj, 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.ite ms():
                        if( name.startswith ('__') and
                        callable(meth) and
                        #not name.startswith ('__getattr') and
                        not name == '__init__'
                        ):
                        m = mk_meth_deleg(o bj, meth, name)
                        proxy_cdict[name] = m

                        # customize proxy class as desired here
                        def __getitem__(sel f, 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.insta ttr = 'proxy_obj instance attribute set by Test'
                        return proxy_obj
                        =============== =============== =============== =============== ==========
                        Sample interaction:
                        [color=blue][color=green][color=darkred]
                        >>> from for_grodrigues import Test
                        >>> a = Test([1,2,3])
                        >>> a[/color][/color][/color]
                        [1, 2, 3][color=blue][color=green][color=darkred]
                        >>> type(a)[/color][/color][/color]
                        <class 'for_grodrigues .list_proxy'>[color=blue][color=green][color=darkred]
                        >>> a.__iter__[/color][/color][/color]
                        <method-wrapper object at 0x00902990>[color=blue][color=green][color=darkred]
                        >>> iter(a)[/color][/color][/color]
                        <listiterator object at 0x00902970>[color=blue][color=green][color=darkred]
                        >>> a.__iter__()[/color][/color][/color]
                        <listiterator object at 0x00902910>[color=blue][color=green][color=darkred]
                        >>> ait = iter(a)
                        >>> ait.next()[/color][/color][/color]
                        1[color=blue][color=green][color=darkred]
                        >>> for i in a: print i,[/color][/color][/color]
                        ...
                        1 2 3

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

                        [color=blue][color=green][color=darkred]
                        >>> b = Test(None)
                        >>> type(b)[/color][/color][/color]
                        <class 'for_grodrigues .NoneType_proxy '>[color=blue][color=green][color=darkred]
                        >>> b[/color][/color][/color]
                        None[color=blue][color=green][color=darkred]
                        >>> b[2][/color][/color][/color]
                        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__:
                        [color=blue][color=green][color=darkred]
                        >>> c = Test('abcd')
                        >>> for x in c: print x,[/color][/color][/color]
                        ...
                        a b [Test-modified 'c'] d

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

                        By inheriting from the object's class, we pick up base class methods:
                        [color=blue][color=green][color=darkred]
                        >>> class A(object):[/color][/color][/color]
                        ... def afoo(self): return 'afoo()'
                        ...[color=blue][color=green][color=darkred]
                        >>> class B(A):[/color][/color][/color]
                        ... def bfoo(self): return 'bfoo()'
                        ...[color=blue][color=green][color=darkred]
                        >>> b = B()
                        >>> pb = Test(b)
                        >>> pb.bfoo()[/color][/color][/color]
                        'bfoo()'[color=blue][color=green][color=darkred]
                        >>> pb.afoo()[/color][/color][/color]
                        'afoo()'[color=blue][color=green][color=darkred]
                        >>> type(pb)[/color][/color][/color]
                        <class '__main__.B_pro xy'>

                        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

                        Comment

                        • Bengt Richter

                          #13
                          Re: Bug? If not, how to work around it?

                          On 7 Aug 2003 15:54:33 -0700, mis6@pitt.edu (Michele Simionato) wrote:
                          [color=blue]
                          >Alex Martelli <aleax@aleax.it > wrote in message news:<t%vYa.291 60$an6.1020776@ news1.tin.it>.. .[color=green]
                          >> "__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.
                          >>[/color]
                          >
                          >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__(sel f,name):
                          > if name=='__len__' : return lambda self:0
                          >
                          >class F: __metaclass__=M
                          >[color=green][color=darkred]
                          >>>> f=F()
                          >>>> F.__len__(f) # okay 0
                          >>>> len(f) # TypeError: len() of unsized object[/color][/color]
                          >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__.__l en__(x); I really would like to know how ``len``
                          >(or ``str``, ``repr``, etc.) work.
                          >"""[/color]

                          Not very tested, but maybe you have to write it something like this:
                          [color=blue][color=green][color=darkred]
                          >>> class M(type):[/color][/color][/color]
                          ... def __new__(cls, name, bases, cdict):
                          ... cdict['__len__'] = lambda self:0
                          ... return type.__new__(cl s, name, bases, cdict)
                          ...[color=blue][color=green][color=darkred]
                          >>> class F: __metaclass__ = M[/color][/color][/color]
                          ...[color=blue][color=green][color=darkred]
                          >>> f=F()
                          >>> F.__len__[/color][/color][/color]
                          <unbound method F.<lambda>>[color=blue][color=green][color=darkred]
                          >>> F.__len__(f)[/color][/color][/color]
                          0[color=blue][color=green][color=darkred]
                          >>> len(f)[/color][/color][/color]
                          0
                          [color=blue][color=green][color=darkred]
                          >>> hasattr(M,'__le n__')[/color][/color][/color]
                          False[color=blue][color=green][color=darkred]
                          >>> hasattr(F,'__le n__')[/color][/color][/color]
                          True

                          Or am I missing the point? ;-/
                          [color=blue]
                          >
                          >If you remember, Bjorn Pettersen reported this (or something similar)
                          >months ago. I am saying that this behaviour is not documented, at
                          >least AFAIK.
                          >[/color]

                          Regards,
                          Bengt Richter

                          Comment

                          • Michele Simionato

                            #14
                            Re: Bug? If not, how to work around it?

                            bokr@oz.net (Bengt Richter) wrote in message news:<bgv1bf$eo i$0@216.39.172. 122>...[color=blue]
                            > Not very tested, but maybe you have to write it something like this:
                            >[color=green][color=darkred]
                            > >>> class M(type):[/color][/color]
                            > ... def __new__(cls, name, bases, cdict):
                            > ... cdict['__len__'] = lambda self:0
                            > ... return type.__new__(cl s, name, bases, cdict)
                            > ...[color=green][color=darkred]
                            > >>> class F: __metaclass__ = M[/color][/color]
                            > ...[color=green][color=darkred]
                            > >>> f=F()
                            > >>> F.__len__[/color][/color]
                            > <unbound method F.<lambda>>[color=green][color=darkred]
                            > >>> F.__len__(f)[/color][/color]
                            > 0[color=green][color=darkred]
                            > >>> len(f)[/color][/color]
                            > 0
                            >[color=green][color=darkred]
                            > >>> hasattr(M,'__le n__')[/color][/color]
                            > False[color=green][color=darkred]
                            > >>> hasattr(F,'__le n__')[/color][/color]
                            > True
                            >
                            > Or am I missing the point? ;-/
                            >
                            > Regards,
                            > Bengt Richter[/color]

                            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

                            Comment

                            • Michele Simionato

                              #15
                              Re: Bug? If not, how to work around it?

                              Gonçalo Rodrigues <op73418@mail.t elepac.pt> wrote in message news:<qp25jv04o ct9v44vudgi5lt4 t1ukqtnjt9@4ax. com>...[color=blue]
                              > 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__.[/color]

                              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__ :
                              [color=blue][color=green][color=darkred]
                              >>> class M(type):[/color][/color][/color]
                              def __getattr__(cls ,name):
                              if name=='__iter__ ': return lambda self:iter([])[color=blue][color=green][color=darkred]
                              >>> class C: __metaclass__=M
                              >>> c=C()
                              >>> iter(c)[/color][/color][/color]
                              Traceback (most recent call last):
                              File "<pyshell#1 0>", line 1, in -toplevel-
                              iter(c)
                              TypeError: iteration over non-sequence[color=blue][color=green][color=darkred]
                              >>> C.__iter__(c)[/color][/color][/color]
                              <listiterator object at 0x00A3DB30>

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

                              Michele

                              Comment

                              Working...