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: 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: 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: @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: Armin Rigo (arigo) Date: 2005-12-26 17:27 Message: Logged In: YES user_id=4771 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() hello >>> 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 Message: Logged In: YES user_id=6133 This bug *may* be related to Bug#1066490 ---------------------------------------------------------------------- Comment By: Raymond Hettinger (rhettinger) Date: 2005-12-17 04: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 22: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 21: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 _______________________________________________ Python-bugs-list mailing list Unsubscribe: http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com