python中的方法委派

问题描述 投票:5回答:2

我正在编写一个用于编排AWS集群的小框架,并且有一些常见的层次结构模式一次又一次出现。一种这样的模式是将一组实例收集到一个更大的对象中,然后将一些方法直接委托给所有实例。因此,我不是一遍又一遍地复制和粘贴相同的样板代码,而是使用以下模式对其进行抽象:

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

是否有更好的方式或模式来完成授权?

python python-2.7 reflection delegation message-passing
2个回答
9
投票

当遍历全班hirarchy并且找不到属性时,调用__getattr__。因此,最好一次生成方法并将其存储在类中。然后找到该方法下次花费的时间更少。

>>> 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>

在这里,您可以看到代码的改编:

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']

1
投票

我一直在研究这个并找到两个解决方案。使用装饰器更改类并创建委托者,或使用委托者的描述符。我从第一个开始,然后演变到我更喜欢的第二个,所以我将从它开始。两者都可以在这里找到:https://gist.github.com/dhilst/7435a09b4419da349bb4cc4ae855a451与doctests :)

Using descriptors

描述符是可以获取和设置的东西。在这种情况下,我们对描述符的可获取能力感兴趣。委托描述符定义如下

class DelegateTo:
    def __get__(self, object, objecttype):
        if self.method is not None:
            return getattr(getattr(obj, self.to), self.method)

        for method, v in obj.__class__.__dict__.items():
            if v is self:
                self.method = method
                return getattr(getattr(obj, self.to), method)

就像这样使用

class Foo:
    upper = DelegateTo('v')
    __len__ = DelegateTo('l')
    __iter__ = DelegateTo('l')
    def __init__(self, v, l):
         self.v = v
         self.l = l

要调用描述符,只需调用方法Foo('hello').upper()。魔术方法也有效len(Foo('', [1,2,3,4]))返回4.上面的要点链接有一个更强大的实现,但基本是相同的。

Using decorators

每当您需要以重复的方式更改类行为时,装饰器就是候选者。在这种情况下,装饰器将在类中调用setattr来创建委托者。

def delegate(to, *methods):
    def dec(klass):
        def create_delegator(method):
            def delegator(self, *args, **kwargs):
                obj = getattr(self, to)
                m = getattr(obj, method)
                return m(*args, **kwargs)
            return delegator
        for m in methods:
            setattr(klass, m, create_delegator(m))
        return klass
    return dec

用法也很简单,只需要装饰类,就像你想要的那样。装饰器将在原位修改类,以便返回相同的类。

这是一种用法

@delegate('v', 'upper', 'lower')
class Foo:
   def __init__(self, v):
       self.v = v

并且委托方法的调用也是透明的Foo('hello').upper()。我更喜欢第二个,因为它对我来说似乎更惯用。装饰器具有支持多种方法的优点,但这也可以在描述符形式上实现。

再次,我真的建议您看一下要点:https://gist.github.com/dhilst/7435a09b4419da349bb4cc4ae855a451文档字符串中有大量示例。只需修改它们并执行脚本即可。

© www.soinside.com 2019 - 2024. All rights reserved.