Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Method delegation in python

I'm writing a small framework for orchestrating AWS clusters and there are some common hierarchical patterns that appear over and over again. One such pattern is gathering a collection of instances into a bigger object and then delegating some methods down to all the instances directly. So instead of copying and pasting the same boilerplate code over and over again I abstracted it with the following pattern:

def __getattr__(self, item):
    if not item in self._allowed_items:
        raise NonDelegatableItem

    def delegator():
        for instance in self.all_instances:
            getattr(instance, item)()

    return delegator

Is there a better way or pattern for accomplishing the delegation?

like image 387
David K. Avatar asked Feb 22 '14 23:02

David K.


People also ask

What is delegation in Python?

Python Questions and Answers. (Continued from previous question...) What is delegation? Delegation is an object oriented technique (also called a design pattern). Let's say you have an object x and want to change the behaviour of just one of its methods.

What does delegate method do?

A delegate is a type that represents references to methods with a particular parameter list and return type. When you instantiate a delegate, you can associate its instance with any method with a compatible signature and return type. You can invoke (or call) the method through the delegate instance.

What is difference between delegate and method?

A delegate is a method with a parameter and a return type. A delegate is a type that safely encapsulates a method. Delegates are object-oriented, type safe, and secure.


1 Answers

__getattr__ is called when the whole class hirarchy is traversed and the attribute is not found. So it is better to generate the method once and store it in the class. Then finding the method takes less time next time.

>>> X.a

Traceback (most recent call last):
  File "<pyshell#15>", line 1, in <module>
    X.a
AttributeError: class X has no attribute 'a'
>>> x.a
new delegator
<function delegator at 0x02937D30>
>>> x.a
<bound method X.delegator of <__main__.X instance at 0x028DBC60>>
>>> X.a
<unbound method X.delegator>

Here you can see the adaption of your code to do that:

class NonDelegatableItem(AttributeError):
    pass

class X:
    def __getattr__(self, method_name):
        self.check_method_name_is_delegator(method_name)
        return self.create_delegator(method_name)

    def check_method_name_is_delegator(self, method_name):
        if method_name not in self._allowed_items:
            raise NonDelegatableItem('{} can not be delegated'.format(method_name))

    @classmethod
    def create_delegator(cls, method_name):
        print 'new delegator'
        def delegator(self, *args, **kw):
            self.check_method_name_is_delegator(method_name)
            for instance in self.all_instances:
                getattr(instance, method_name)(*args, **kw)
        setattr(cls, method_name, delegator)
        return delegator


x = X()

x._allowed_items = ['a', 'b']
like image 139
User Avatar answered Oct 21 '22 02:10

User