While delegation is possible, it is indeed not common.
In many cases where you might reach to delegation, you could also use multiple inheritance. Ruby doesn't have multiple inheritance, therefore it must use other approaches such as mixins or delegation. Python has more flexible semantics here.
Delegation based on
__getattribute__ is complex, and needs a fair understanding of Python's low-level details to implement properly. This interacts with the descriptor protocol, and introspection features like
help(), and with other dunder methods such as
__add__: you cannot implicitly delegate dunder methods, but would have to implement a method that performs the delegation.
This complexity is perceived as un-Pythonic. “Explicit is better than implicit.”
Instead of delegation through
__getattr__, you could just manually implement the methods or properties that you want to delegate. You can use metaprogramming (via metaclasses or class decorators) to simplify this, or use the descriptor protocol to create class members that perform the delegation when accessed through an instance.
Delegation is slow, especially when based on reflection or metaprogramming features.
Descriptors are objects that implement a class member. They are invoked in different ways depending on whether a class or instance member is accessed. Functions and properties are commonly used descriptor implementations, but user-defined descriptors can be created. Anything that has a
__get__ method may be used as a descriptor. In one project I implemented a descriptor to perform delegation, e.g. used like:
delegated_method = delegate('TargetType', 'delegated_method', to='_target')
def __init__(self, target):
self._target = target
# Foo().delegated_method => Foo()._target.delegated_method
# Foo.delegated_method => returns the descriptor
# help(Foo.delegated_method) # works
In the end, the complexity and performance impact of using descriptors was too much in that project, and I just spelled out the delegation explicitly instead.