python中的方法委托

2024-05-16 21:44:01 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在编写一个小框架,用于编排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

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


Tags: 对象实例方法instanceinself框架aws
2条回答

我一直在研究这个问题,找到了两个解决方案。使用decorator更改类并创建delegator,或者使用delegator的描述符。我从第一个开始,然后发展到我更喜欢的第二个,所以我将从它开始。两者都可以在这里找到:https://gist.github.com/dhilst/7435a09b4419da349bb4cc4ae855a451和doctests:)

--编辑--

对于任何感兴趣的人,我把它做成一个图书馆:https://pypi.org/project/delegateto/

使用描述符

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

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()。Magic方法也可以工作len(Foo('', [1,2,3,4]))返回4。上面的gist链接有一个更强大的实现,但基本原理是相同的。

使用装饰器

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

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

用法也很简单,只要装饰一下班级,就可以了。decorator将就地修改类,以便返回相同的类。

这里有一个用法

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

委托方法的调用也是透明的Foo('hello').upper()。我更喜欢第二个,因为它对我来说更习惯。decorator具有支持多个方法的优势,但这也可以在描述符表单上实现。

同样,我真的建议您看看要点:https://gist.github.com/dhilst/7435a09b4419da349bb4cc4ae855a451docstring中有很多示例。只要修改它们并执行脚本就可以了。

更新:

对于任何感兴趣的人,我都会把它做成pip包https://pypi.org/project/delegateto/

问候

当遍历整个类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']

相关问题 更多 >