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

SourceForge.net noreply at sourceforge.net
Mon Dec 19 16:25:55 CET 2005


Bugs item #1382740, was opened at 2005-12-16 13:18
Message generated for change (Comment added) made by kquick
You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=105470&aid=1382740&group_id=5470

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: Open
Resolution: None
Priority: 5
Submitted By: Kevin Quick (kquick)
Assigned to: Guido van Rossum (gvanrossum)
Summary: len() on class broken

Initial Comment:
With the following python input:

class A:
    @classmethod
    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 
declaration.


----------------------------------------------------------------------

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

Message:
Logged In: YES 
user_id=6133

This bug *may* be related to Bug#1066490

----------------------------------------------------------------------

Comment By: Raymond Hettinger (rhettinger)
Date: 2005-12-16 21:40

Message:
Logged In: YES 
user_id=80475

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
access.  

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)
        try:
            f = gettattr(o, 'somefunc')
        except AttributeError:
            raise TypeError
        else:
            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 15:52

Message:
Logged In: YES 
user_id=6133

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
      correct...

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:
  @staticmethod
  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 14:43

Message:
Logged In: YES 
user_id=1188172

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: 
https://sourceforge.net/tracker/?func=detail&atid=105470&aid=1382740&group_id=5470


More information about the Python-bugs-list mailing list