[EMAIL PROTECTED] wrote: > On Oct 12, 12:19 pm, James Stroud <[EMAIL PROTECTED]> wrote: >> [EMAIL PROTECTED] wrote: >>> I have a container class A and I want to add functionality to it by >>> using a decorator class B, as follows: >>> class A(object): >>> def __len__(self): >>> return 5 >>> class B(object): >>> def __init__(self, a): >>> self._a = a >>> def __getattr__(self, attr): >>> return getattr(self._a, attr) >>> def other_methods(self): >>> blah blah blah >>> I was expecting len(B) to return 5 but I get >>> AttributeError: type object 'B' has no attribute '__len__' >>> instead. >>> I was expecting len() to call B.__len__() which would invoke >>> B.__getattr__ to call A.__len__ but __getattr__ is not being called. >>> I can work around this, but I am curious if anyone knows _why_ >>> __getattr__ is not being called in this situation. >>> Thanks >>> Tim >> The why part is that __len__ is an unbound method of the class, not an >> unbound method of the class's metaclass. Also, your code above makes >> absolutely no association between classes A and B, which is the most >> fundamental reason. > > Just so I'm clear on this: Are you saying that the problem with my > original code is that len() is equivalent to type(x).__len__(x) that > looks for a class attribute rather than an instance attribute as my > code requires? > > I don't own class A and they are generated by function calls rather > than instantiated directly, that's why I'm decorating them instead of > inheriting. The crux of the (academic) question is why does len() > expect '__len__' to be a class attribute instead of an instance > attribute, and why is this a good idea?
You are taking the length of the *class* (whatever that means) with "len(B)", so B must have a method __len__() bound to itself, which would be an unbound method of B's class (which I am calling the metaclass of B, since B is a class--a class's class is a metaclass). So you want the unbound __len__() method of A to be the unbound __len__() method of the metaclass of B. This is easier done than said: def long_class_factory(cls, name): class _longmeta(type): __len__ = cls.__len__.im_func return _longmeta(name, (), {}) E.g.: py> def long_class_factory(cls, name): ... class _longmeta(type): ... __len__ = cls.__len__.im_func ... return _longmeta(name, (), {}) ... py> class A(object): ... def __len__(self): ... return 5 ... py> B = long_class_factory(A, 'B') py> B <class '__main__.B'> py> len(B) 5 Of course you can now inherit from B: class C(B): pass E.g.: py> class C(B): pass ... py> len(C) 5 James -- James Stroud UCLA-DOE Institute of Genomics and Proteomics Box 951570 Los Angeles, CA 90095 http://www.jamesstroud.com -- http://mail.python.org/mailman/listinfo/python-list