我正在编写一个用于编排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
是否有更好的方式或模式来完成授权?
当遍历全班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']
我一直在研究这个并找到两个解决方案。使用装饰器更改类并创建委托者,或使用委托者的描述符。我从第一个开始,然后演变到我更喜欢的第二个,所以我将从它开始。两者都可以在这里找到:https://gist.github.com/dhilst/7435a09b4419da349bb4cc4ae855a451与doctests :)
描述符是可以获取和设置的东西。在这种情况下,我们对描述符的可获取能力感兴趣。委托描述符定义如下
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.上面的要点链接有一个更强大的实现,但基本是相同的。
每当您需要以重复的方式更改类行为时,装饰器就是候选者。在这种情况下,装饰器将在类中调用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文档字符串中有大量示例。只需修改它们并执行脚本即可。