[ python-Bugs-1382740 ] len() on class broken
SourceForge.net
noreply at sourceforge.net
Sat Dec 17 05:40:47 CET 2005
Bugs item #1382740, was opened at 2005-12-16 15:18
Message generated for change (Comment added) made by rhettinger
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: Raymond Hettinger (rhettinger)
Date: 2005-12-16 23: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 17: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 16: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