A separate issue, the documentation for super () (in Python 3.2) only talks about delegating its method and does not specify that Attributes are searched in the same way for a proxy server. Is this a random omission?
No, this is no coincidence. super() does nothing to search for attributes. The reason is that the attributes of the instance are not associated with a particular class, they are just there. Consider the following:
class A: def __init__(self): self.foo = 'foo set from A' class B(A): def __init__(self): super().__init__() self.bar = 'bar set from B' class C(B): def method(self): self.baz = 'baz set from C' class D(C): def __init__(self): super().__init__() self.foo = 'foo set from D' self.baz = 'baz set from D' instance = D() instance.method() instance.bar = 'not set from a class at all'
Which class owns foo , bar and baz ?
If I wanted to view instance as an instance of C, should it have a baz attribute before calling method ? How about later?
If I consider instance as an instance of A, what value should foo ? Should bar be invisible because it was added only to B, or visible because it was set to a value outside the class?
All these questions are nonsense in Python. There is no way to create a system with Python semantics that can provide reasonable answers to them. __init__ is not even special in terms of adding attributes to class instances; this is just a completely normal method, which is considered to be called part of the instantiation protocol. Any method (or, indeed, code from another class at all or not from any class at all) can create attributes on any instance to which it refers.
In fact, all instance attributes are stored in the same place:
>>> instance.__dict__ {'baz': 'baz set from C', 'foo': 'foo set from D', 'bar': 'not set from a class at all'}
It is not possible to determine which ones were originally given by the class or were the last given by which class or whatever measure of ownership you would like. Of course, there is no way to get " A.foo D.foo ", as you would expect from C ++; they are one and the same attribute, and any records on it by one class (or from another place) will compress the value remaining in it by another class.
The consequence of this is that super() does not perform attribute searches in the same way as method searches; he cannot, and no code can write.
In fact, starting with some experiments, neither super nor Sven Delegate actually support direct attribute searches at all!
class A: def __init__(self): self.spoon = 1 self.fork = 2 def foo(self): print('A.foo') class B(A): def foo(self): print('B.foo') b = B() d = Delegate(A, b) s = super(B, b)
Then both work as expected for the methods:
>>> d.foo() A.foo >>> s.foo() A.foo
But:
>>> d.fork Traceback (most recent call last): File "<pyshell#43>", line 1, in <module> d.fork File "/tmp/foo.py", line 6, in __getattr__ x = getattr(self._delegate_cls, name) AttributeError: type object 'A' has no attribute 'fork' >>> s.spoon Traceback (most recent call last): File "<pyshell#45>", line 1, in <module> s.spoon AttributeError: 'super' object has no attribute 'spoon'
Thus, they both really only work for calling some methods, and not for passing to an arbitrary third code, in order to claim the role of an instance of the class that you want to delegate.
They do not behave the same in the presence of multiple inheritance. Given:
class Delegate: def __init__(self, cls, obj): self._delegate_cls = cls self._delegate_obj = obj def __getattr__(self, name): x = getattr(self._delegate_cls, name) if hasattr(x, "__get__"): return x.__get__(self._delegate_obj) return x class A: def foo(self): print('A.foo') class B: pass class C(B, A): def foo(self): print('C.foo') c = C() d = Delegate(B, c) s = super(C, c)
Then:
>>> d.foo() Traceback (most recent call last): File "<pyshell#50>", line 1, in <module> d.foo() File "/tmp/foo.py", line 6, in __getattr__ x = getattr(self._delegate_cls, name) AttributeError: type object 'B' has no attribute 'foo' >>> s.foo() A.foo
Because Delegate ignores the full MRO of any _delegate_obj class, it is an instance only using the MRO _delegate_cls . While super does what you asked in the question, but the behavior seems rather strange: it does not complete the instance of C to give it an instance of B, because direct instances of B do not have foo .
Here is my attempt:
class MROSkipper: def __init__(self, cls, obj): self.__cls = cls self.__obj = obj def __getattr__(self, name): mro = self.__obj.__class__.__mro__ i = mro.index(self.__cls) if i == 0:
I rely on the __mro__ attribute of classes to correctly determine where to start, and then just use super . You can go through the MRO chain from now on by checking the __dict__ class instead of methods if it is too difficult to go back to one step to use super .
I did not try to handle unusual attributes; those that were implemented with descriptors (including properties), or those magic methods that looked behind the scenes of Python, which often begin in the class, and not directly with the instance. But this behaves the same way you asked moderately well (with the warning set out in the commercial break in the first part of my message, searching for attributes in this way will not give you any other results than finding them directly in the instance).