![](/img/trans.png)
[英]Is there any design pattern for implementing decoration of methods of child classes of an abstract class?
[英]Delegation design pattern with abstract methods in python
我有以下类,通过附加的DelegatorParent类来实现“委托设计模式”:
class DelegatorParent():
def __init__(self):
self.a = 'whatever'
class ConcreteDelegatee():
def myMethod(self):
return 'myMethod'
class Delegator(DelegatorParent):
def __init__(self):
self.delegatee = ConcreteDelegatee()
DelegatorParent.__init__(self)
def __getattr__(self, attrname):
return getattr(self.delegatee, attrname)
a = Delegator()
result = a.myMethod()
一切看起来都很好。
现在,我想在DelegatorParent中放置一个抽象方法,以确保始终定义“ myMethod”。
from abc import ABCMeta, abstractmethod
class DelegatorParent():
__metaclass__ = ABCMeta
@abstractmethod
def myMethod(self):
pass
def __init__(self):
self.a = 'whatever'
class ConcreteDelegatee():
def myMethod(self):
return 'myMethod'
class Delegator(DelegatorParent):
def __init__(self):
self.delegatee = ConcreteDelegatee()
DelegatorParent.__init__(self)
def __getattr__(self, attrname):
return getattr(self.delegatee, attrname)
# This method seems unnecessary, but if I erase it an exception is
# raised because the abstract method's restriction is violated
def myMethod(self):
return self.delegatee.myMethod()
a = Delegator()
result = a.myMethod()
您能帮我找到一种从“委托人”中删除“ myMethod”的“优雅”方法吗...直觉告诉我这在某种程度上是多余的(考虑到定义了自定义getattr方法)。
更重要的是,请注意,使用此实现,如果我忘记在ConcreteDelegatee中定义myMethod,则程序会编译,但是如果我调用Delegator.myMethod(),它可能会在运行时崩溃,这正是我想通过在其中使用抽象方法来避免的事情DelegatorParent。
显然,一个简单的解决方案是将@abstractmethod移至Delegator类,但是我想避免这样做,因为在我的程序中DelegatorParent是一个非常重要的类(而Delegator只是一个辅助类)。
您可以决定自动实现指定给ConcreteDelegatee
抽象方法。
对于每个抽象方法,请检查其名称是否存在于ConcreteDelegatee
类中,并将该方法实现为该类方法的委托。
from abc import ABCMeta, abstractmethod
class DelegatorParent(object):
__metaclass__ = ABCMeta
def __init__(self):
self.a = 'whatever'
@abstractmethod
def myMethod(self):
pass
class Delegatee(object):
pass
class ConcreteDelegatee(Delegatee):
def myMethod(self):
return 'myMethod'
def myMethod2(self):
return 'myMethod2'
class Delegator(DelegatorParent):
def __new__(cls, *args, **kwargs):
implemented = set()
for name in cls.__abstractmethods__:
if hasattr(ConcreteDelegatee, name):
def delegated(this, *a, **kw):
meth = getattr(this.delegatee, name)
return meth(*a, **kw)
setattr(cls, name, delegated)
implemented.add(name)
cls.__abstractmethods__ = frozenset(cls.__abstractmethods__ - implemented)
obj = super(Delegator, cls).__new__(cls, *args, **kwargs)
obj.delegatee = ConcreteDelegatee()
return obj
def __getattr__(self, attrname):
# Called only for attributes not defined by this class (or its bases).
# Retrieve attribute from current behavior delegate class instance.
return getattr(self.delegatee, attrname)
# All abstract methods are delegared to ConcreteDelegatee
a = Delegator()
print(a.myMethod()) # correctly prints 'myMethod'
print(a.myMethod2()) #correctly prints 'myMethod2'
这解决了主要问题(防止ConcreteDelegatee
忘记定义myMethod
)。 如果您忘记实现其他抽象方法,则仍将对其进行检查。
__new__
方法负责委派,使您的__init__
可以释放它。
由于使用ABCMeta
,因此必须定义抽象方法。 可以从__abstractmethods__
集合中删除您的方法,但这是一个frozenset
。 无论如何,它涉及列出所有抽象方法。
因此,可以使用一个简单的描述符来代替__getattr__
。
例如:
class Delegated(object):
def __init__(self, attrname=None):
self.attrname = attrname
def __get__(self, instance, owner):
if instance is None:
return self
delegatee = instance.delegatee
return getattr(delegatee, self.attrname)
class Delegator(DelegatorParent):
def __init__(self):
self.delegatee = ConcreteDelegatee()
DelegatorParent.__init__(self)
myMethod = Delegated('myMethod')
这是一个优点:开发人员拥有明确的信息,可以委派“ myMethod”。
如果你试试:
a = Delegator()
result = a.myMethod()
有用! 但是,如果您忘记在Delegator
类中实现myMethod
,则会遇到经典错误:
Traceback (most recent call last):
File "script.py", line 40, in <module>
a = Delegator()
TypeError: Can't instantiate abstract class Delegator with abstract methods myMethod
编辑
该实现可以概括如下:
class DelegatorParent():
__metaclass__ = ABCMeta
@abstractmethod
def myMethod1(self):
pass
@abstractmethod
def myMethod2(self):
pass
def __init__(self):
self.a = 'whatever'
class ConcreteDelegatee1():
def myMethod1(self):
return 'myMethod1'
class ConcreteDelegatee2():
def myMethod2(self):
return 'myMethod2'
class DelegatedTo(object):
def __init__(self, attrname):
self.delegatee_name, self.attrname = attrname.split('.')
def __get__(self, instance, owner):
if instance is None:
return self
delegatee = getattr(instance, self.delegatee_name)
return getattr(delegatee, self.attrname)
class Delegator(DelegatorParent):
def __init__(self):
self.delegatee1 = ConcreteDelegatee1()
self.delegatee2 = ConcreteDelegatee2()
DelegatorParent.__init__(self)
myMethod1 = DelegatedTo('delegatee1.myMethod1')
myMethod2 = DelegatedTo('delegatee2.myMethod2')
a = Delegator()
result = a.myMethod2()
在这里,我们可以指定委托人名称和委托人方法。
这是我目前的解决方案。 它解决了主要问题(避免了忘记定义myMethod来防止ConcreteDelegatee),但是我仍然不确定,因为我仍然需要在Delegator中定义myMethod,这似乎是多余的
from abc import ABCMeta, abstractmethod
class DelegatorParent(object):
__metaclass__ = ABCMeta
def __init__(self):
self.a = 'whatever'
@abstractmethod
def myMethod(self):
pass
class Delegatee(object):
def checkExistence(self, attrname):
if not callable(getattr(self, attrname, None)):
error_msg = "Can't instantiate " + str(self.__class__.__name__) + " without abstract method " + attrname
raise NotImplementedError(error_msg)
class ConcreteDelegatee(Delegatee):
def myMethod(self):
return 'myMethod'
def myMethod2(self):
return 'myMethod2'
class Delegator(DelegatorParent):
def __init__(self):
self.delegatee = ConcreteDelegatee()
DelegatorParent.__init__(self)
for method in DelegatorParent.__abstractmethods__:
self.delegatee.checkExistence(method)
def myMethod(self, *args, **kw):
return self.delegatee.myMethod(*args, **kw)
def __getattr__(self, attrname):
# Called only for attributes not defined by this class (or its bases).
# Retrieve attribute from current behavior delegate class instance.
return getattr(self.delegatee, attrname)
# if I forget to implement myMethod inside ConcreteDelegatee,
# the following line will correctly raise an exception saying
# that 'myMethod' is missing inside 'ConcreteDelegatee'.
a = Delegator()
print a.myMethod() # correctly prints 'myMethod'
print a.myMethod2() #correctly prints 'myMethod2'
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.