python 中具有抽象方法的委托设计模式
Delegation design pattern with abstract methods in python
我有以下 classes 实现一个 "Delegation Design Pattern" 和一个额外的 DelegatorParent class:
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()
你能帮我找到一个 "elegant" 从 "Delegator" 中删除 "myMethod" 的方法吗...直觉告诉我它有点多余(考虑到定义了自定义 getattr 方法).
更重要的是,请注意,对于此实现,如果我忘记在 ConcreteDelegatee 中定义 myMethod,程序会编译,但如果我调用 Delegator.myMethod(),它可能会在运行时崩溃,这正是我想要的通过在 DelegatorParent 中使用抽象方法来避免。
显然,一个简单的解决方案是将@abstractmethod 移动到 Delegator class,但我想避免这样做,因为在我的程序中 DelegatorParent 是一个非常重要的 class(而 Delegator 只是辅助 class).
既然使用了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
class 中实现 myMethod
,你就会出现 classic 错误:
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()
在这里,我们可以指定受托人姓名和受托人方法。
这是我目前的解决方案。它解决了主要问题(防止ConcreteDelegatee忘记定义myMethod),但我仍然不相信,因为我仍然需要在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'
您可以决定自动实现委托给 ConcreteDelegatee
的抽象方法。
对于每个抽象方法,检查它的名称是否存在于 ConcreteDelegatee
class 中,并将此方法实现为此 class 方法的委托。
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__
有空去做。
我有以下 classes 实现一个 "Delegation Design Pattern" 和一个额外的 DelegatorParent class:
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()
你能帮我找到一个 "elegant" 从 "Delegator" 中删除 "myMethod" 的方法吗...直觉告诉我它有点多余(考虑到定义了自定义 getattr 方法).
更重要的是,请注意,对于此实现,如果我忘记在 ConcreteDelegatee 中定义 myMethod,程序会编译,但如果我调用 Delegator.myMethod(),它可能会在运行时崩溃,这正是我想要的通过在 DelegatorParent 中使用抽象方法来避免。
显然,一个简单的解决方案是将@abstractmethod 移动到 Delegator class,但我想避免这样做,因为在我的程序中 DelegatorParent 是一个非常重要的 class(而 Delegator 只是辅助 class).
既然使用了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
class 中实现 myMethod
,你就会出现 classic 错误:
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()
在这里,我们可以指定受托人姓名和受托人方法。
这是我目前的解决方案。它解决了主要问题(防止ConcreteDelegatee忘记定义myMethod),但我仍然不相信,因为我仍然需要在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'
您可以决定自动实现委托给 ConcreteDelegatee
的抽象方法。
对于每个抽象方法,检查它的名称是否存在于 ConcreteDelegatee
class 中,并将此方法实现为此 class 方法的委托。
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__
有空去做。