[ python-Bugs-1382740 ] len() on class broken

SourceForge.net noreply at sourceforge.net
Mon Dec 26 18:27:24 CET 2005

Bugs item #1382740, was opened at 2005-12-16 20:18
Message generated for change (Comment added) made by arigo
You can respond by visiting: 

Please note that this message will contain a full copy of the comment thread,
including the initial issue submission, for this request,
not just the latest update.
Category: Python Interpreter Core
Group: Python 2.4
>Status: Closed
>Resolution: Invalid
Priority: 5
Submitted By: Kevin Quick (kquick)
>Assigned to: Nobody/Anonymous (nobody)
Summary: len() on class broken

Initial Comment:
With the following python input:

class A:
    def __len__(cls):
        return 12

print '1',A.__len__()
print '2',A().__len__()
print '3',len(A())
print '4',len(A)

The output always breaks for '4' with 'TypeError: len 
of unsized object'

Same result for @staticmethod or normal instance method 


>Comment By: Armin Rigo (arigo)
Date: 2005-12-26 17:27

Logged In: YES 

Un-assigning Guido.

This is a known documentation bug: all this is expected, but
under-documented.  Indeed, len(x) calls the special method
__len__ of 'x', but that's quite different from the
expression "x.__len__()".   Indeed, the real definition of
"calling a special method" on an object 'x' is to look up
the name "__len__" in the dict of type(x), then in the dict
of the parent types in MRO order.  It's really not the same
thing as an attribute lookup.

This definition makes sense for the Python language; it has
nothing to do with the C slots.  The reason is more
fundamental.  Consider, say, what would occur if "repr(x)"
was equivalent to "x.__repr__()":

>>> class X(object):
...    def __repr__(self):
...        return "hello"
>>> X()
>>> X
TypeError: __repr__() takes exactly 1 argument (0 given)

because X.__repr__() is just an unbound method call with a
missing argument.


Comment By: Kevin Quick (kquick)
Date: 2005-12-19 15:25

Logged In: YES 

This bug *may* be related to Bug#1066490


Comment By: Raymond Hettinger (rhettinger)
Date: 2005-12-17 04:40

Logged In: YES 

Guido, this issue arises throughout the language in various
guises (for instance, it applies to __neg__ as well as
__len__).  It comes-up whenever built-in functions or
operators bypass attribute lookup in favor of direct slot

Much of the code in abstract.c is in the form:

   def PyObject_SomeFunc(o):
        if slots.somefunc is not None:
            return slots.somefunc(o)
        raise TypeError

If we cared about this (and I'm not sure we do), the
solution is to make the abstract.c functions smarter:

   def PyObject_SomeFunc(o):
        if slots.somefunc is not None:
            return slots.somefunc(o)
            f = gettattr(o, 'somefunc')
        except AttributeError:
            raise TypeError
            return f()

The advantage of the change is restoring the symmetry
between len(o) and o.__len__() where the method definition
is available via attribute lookup but not via a slot.  The
thought is to keep the speedy access as default, but if that
fails, then do a normal attribute lookup before barfing back
an error message.  This is precedent for this solution
elsewhere in the codebase (though I don't remember where at
the moment).  OTOH, I'm not sure we care.

 pervades the code in abstract.c which is in the form:

   def PyObject_Size


Comment By: Kevin Quick (kquick)
Date: 2005-12-16 22:52

Logged In: YES 

That would indeed solve '4', but has a non-orthogonality of 
attribute propagation that I don't understand and which 
seems inconsistent.

In my original:
  '1' demonstrates that __len__ is indeed in the dictionary
      for the class.
  '2' shows the expected attribute propagation effects: if
      at attribute is not found in the instance dictionary,
      the class dictionary is checked.
  '3' shows that len is implemented (generally) by looking
      for a __len__ method on the object in question.
  '4' confuses me, because it means that '3' isn't quite

With your metaclass solution (using "__metaclass__ = meta" 
:) it does indeed make '4' work, and '1', but '2' and '3' 
now do not work, showing that instance-->class propagation
does not follow instance-->class-->metaclass.  Or something

Approaching this a different way:

My understanding of @classmethod (or perhaps more properly
@staticmethod) is that it allows "constant" methods that are
independent of the particular object instance.  So if I had:

class C:
  def f(): return 1

then both C.f() and C().f() are valid and return 1.

Why the len() translation to __len__ works *differently*
is strange.  I'm still defining a method, just one that
I want Python to use for any len(A) or len(A()) refs,
translating those to A.__len__() or A().__len__() and using 
those just as for C and f, respectively.


Comment By: Reinhold Birkenfeld (birkenfeld)
Date: 2005-12-16 21:43

Logged In: YES 

You want to use a metaclass:

class meta(type):
    def __len__(cls):
        return 12

class B:
    __metaclass__ = B

print len(B)


You can respond by visiting: 

More information about the Python-bugs-list mailing list