python中的方法委派

dav*_*k01 6 python reflection delegation message-passing python-2.7

我正在编写一个用于编排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
Run Code Online (Sandbox Code Playgroud)

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

gec*_*kos 11

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

- 编辑 -

对于任何感兴趣的人,我将其作为一个库:https : //pypi.org/project/delegateto/

gist 实现中存在错误,人们在 github 上对此做出了贡献,pypi 项目已更新,gist 未更新。我强烈建议您使用 pypi 版本。

使用描述符

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

class DelegateTo:
    def __init__(self, to, method=None):
        self.to = to
        self.method = method
    def __get__(self, obj, 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)
Run Code Online (Sandbox Code Playgroud)

并且像这样使用

class Foo:
    upper = DelegateTo('v')
    __len__ = DelegateTo('l')
    __iter__ = DelegateTo('l')
    def __init__(self, v, l):
         self.v = v
         self.l = l
Run Code Online (Sandbox Code Playgroud)

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

使用装饰器

每次你需要以重复的方式改变类的行为时,装饰器都是一个候选者。在这种情况下,装饰器将调用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
Run Code Online (Sandbox Code Playgroud)

用法也很简单,只要装饰类,想多少次就多少。装饰器将就地修改类,以便返回相同的类。

这是一个用法

@delegate('v', 'upper', 'lower')
class Foo:
   def __init__(self, v):
       self.v = v
Run Code Online (Sandbox Code Playgroud)

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

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

- 编辑 -

对于任何感兴趣的机构,我将其作为一个 pip 包https://pypi.org/project/delegateto/

- 编辑 -

gist 实现中存在错误,人们在 github 上对此做出了贡献,pypi 项目已更新,gist 未更新。我强烈建议您使用 pypi 版本。

问候


Use*_*ser 9

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

>>> 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>
Run Code Online (Sandbox Code Playgroud)

在这里,您可以看到代码的适应性:

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']
Run Code Online (Sandbox Code Playgroud)