[英]Delegation design pattern with abstract methods in python
I have the following classes implementing a "Delegation Design Pattern" with an additional DelegatorParent class: 我有以下类,通过附加的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()
Everything looks fine. 一切看起来都很好。
Now I would like to put an abstract method in DelegatorParent, to ensure that "myMethod" is always defined. 现在,我想在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()
Can you help me find an "elegant" way to remove "myMethod" from "Delegator"... Intuition tells me that it is somehow redundant (considering that a custom getattr method is defined). 您能帮我找到一种从“委托人”中删除“ myMethod”的“优雅”方法吗...直觉告诉我这在某种程度上是多余的(考虑到定义了自定义getattr方法)。
And more importantly, notice that with this implementation, if I forget to define myMethod in ConcreteDelegatee the program compiles, but it may crash in runtime if I call Delegator.myMethod(), which is exactly what I wanted to avoid by using abstract methods in DelegatorParent. 更重要的是,请注意,使用此实现,如果我忘记在ConcreteDelegatee中定义myMethod,则程序会编译,但是如果我调用Delegator.myMethod(),它可能会在运行时崩溃,这正是我想通过在其中使用抽象方法来避免的事情DelegatorParent。
Obviously a simple solution would be to move @abstractmethod to the Delegator class, but I want to avoid doing that because in my program DelegatorParent is a very important class (and Delegator is just an auxiliary class). 显然,一个简单的解决方案是将@abstractmethod移至Delegator类,但是我想避免这样做,因为在我的程序中DelegatorParent是一个非常重要的类(而Delegator只是一个辅助类)。
You can decide to automatically implement abstract methods delegared to ConcreteDelegatee
. 您可以决定自动实现指定给
ConcreteDelegatee
抽象方法。
For each abstract method, check if it's name exist in the ConcreteDelegatee
class and implement this method as a delegate to this class method. 对于每个抽象方法,请检查其名称是否存在于
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'
This solves the main problem (prevent ConcreteDelegatee
from forgetting to define myMethod
). 这解决了主要问题(防止
ConcreteDelegatee
忘记定义myMethod
)。 Other abstract methods are still checked if you forgot to implement them. 如果您忘记实现其他抽象方法,则仍将对其进行检查。
The __new__
method is in charge of the delegation, that frees your __init__
to do it. __new__
方法负责委派,使您的__init__
可以释放它。
Since you use ABCMeta
, you must defined the abstract methods. 由于使用
ABCMeta
,因此必须定义抽象方法。 One could remove your method from the __abstractmethods__
set, but it is a frozenset
. 可以从
__abstractmethods__
集合中删除您的方法,但这是一个frozenset
。 Anyway, it involves listing all abstract methods. 无论如何,它涉及列出所有抽象方法。
So, instead of playing with __getattr__
, you can use a simple descriptor. 因此,可以使用一个简单的描述符来代替
__getattr__
。
For instance: 例如:
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')
An advantage here: the developer has the explicit information that "myMethod" is delegated. 这是一个优点:开发人员拥有明确的信息,可以委派“ myMethod”。
If you try: 如果你试试:
a = Delegator()
result = a.myMethod()
It works! 有用! But if you forget to implement
myMethod
in Delegator
class, you have the classic error: 但是,如果您忘记在
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
Edit 编辑
This implementation can be generalized as follow: 该实现可以概括如下:
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()
Here, we can specify the delegatee name and delegatee method. 在这里,我们可以指定委托人名称和委托人方法。
Here is my current solution. 这是我目前的解决方案。 It solves the main problem (prevent ConcreteDelegatee from forgetting to define myMethod), but I'm still not convinced because I still need to define myMethod inside Delegator, which seems redundant
它解决了主要问题(避免了忘记定义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.