首先:您错误地使用了abc 模块(see the docs)。您的课程 A 应该有 abc.ABCMeta 作为元类。因此,如果您已经在使用元类,则可以将其扩展为您的优势。
从abc.ABCMeta 继承以使abstractmethod 工作并装饰do_thing 的元类:
from abc import ABCMeta, abstractmethod
class DecoratingMeta(ABCMeta):
def __new__(cls, *args):
new_class = super(DecoratingMeta, cls).__new__(cls, *args)
# decorating do_thing manually
new_class.do_thing = new_class.do_thing_decorator(new_class.do_thing)
return new_class
现在你的抽象基类带有一个不做任何事情的默认检查装饰器:
# class Abstract(metaclass=ABCMeta): in python3
class Abstract(object):
__metaclass__ = DecoratingMeta # remove this line in python3
@abstractmethod
def do_thing(self, input):
pass
@classmethod
def do_thing_decorator(cls, function):
return function # default decorator that does nothing
注意do_thing_decorator 在这种情况下必须是类方法。
对于在python3 和python2 中工作的元类,请参阅six。
您的 Checker 类仅实现特定检查器但仍然是抽象的:
class Checker(Abstract):
@classmethod
def do_thing_decorator(cls, function):
def do_checked_thing(self, input):
check = function(self, input) # NOT self.do_thing(input) else recursion error
if check != 1:
raise ValueError("Check failed")
return check
return do_checked_thing
请注意,您编写的行 check = do_thing_func(input) 会导致递归错误。
还有你的具体类,带有do_thing 的示例实现:
class Concrete(Checker):
def do_thing(self, input):
return input # sample implementation
您可以验证do_thing(1) 成功和do_thing(2) 失败
c = Concrete()
c.do_thing(1)
try:
c.do_thing(2)
except ValueError:
print("check failed")
这种方法的缺点是您不能将 do_thing_decorator 抽象化。
所以这已经是很多文本了,但是如果您根本不想使用任何元类,还有一个更简单的方法:
编写一个类,使用两个“抽象”方法在do_thing 方法中执行检查:
class Abstract(object):
def do_thing_func(self, input):
raise NotImplementedError()
def check_do_thing(self, result):
raise NotImplementedError()
# don't override this method
def do_thing(self, input):
result = self.do_thing_func(input)
self.check_do_thing(result) # may raise
return result # if it does not raise return the result
请注意do_thing_func 和check_do_thing 并不是真正抽象的,您仍然可以实例化Abstract 类型的对象。如果您需要它们是抽象的,请在此处使用标准的 abc.ABCMeta 元类。
现在创建一个实现check_do_thing的检查器类
class Checker(Abstract):
def check_do_thing(self, result):
if result != 1:
raise ValueError("check failed")
这变得简单多了,因为我们这里不需要装饰器。
最后是实现do_thing_func的具体类
class Concrete(Checker):
def do_thing_func(self, input):
return input # sample implementation
请注意,Concrete 现在必须实现 do_thing_func,但是当您使用该类时,您必须调用 do_thing。
这里的缺点是您仍然可以覆盖 do_thing 从而中断检查。