Python 在修饰ABCMeta子类中的所有方法时强制执行abstractmethod行为
我想实现一个元类,用于包装方法以记录附加信息。但是我还需要有Python 在修饰ABCMeta子类中的所有方法时强制执行abstractmethod行为,python,python-3.x,metaprogramming,metaclass,abstract-methods,Python,Python 3.x,Metaprogramming,Metaclass,Abstract Methods,我想实现一个元类,用于包装方法以记录附加信息。但是我还需要有抽象方法。我试图扩展ABCMeta,但它似乎没有强制执行@abstractmethod装饰器: import types import abc def logfunc(fn, *args, **kwargs): def fncomposite(*args, **kwargs): rt = fn(*args, **kwargs) print("Executed %s" % fn.__name__)
抽象方法
。我试图扩展ABCMeta
,但它似乎没有强制执行@abstractmethod
装饰器:
import types
import abc
def logfunc(fn, *args, **kwargs):
def fncomposite(*args, **kwargs):
rt = fn(*args, **kwargs)
print("Executed %s" % fn.__name__)
return rt
return fncomposite
class LoggerMeta(abc.ABCMeta):
def __new__(cls, clsname, bases, dct):
for name, value in dct.items():
if type(value) is types.FunctionType or type(value) is types.MethodType:
dct[name] = logfunc(value)
return super(LoggerMeta, cls).__new__(cls, clsname, bases, dct)
def __init__(cls, *args, **kwargs):
super(LoggerMeta, cls).__init__(*args, **kwargs)
if cls.__abstractmethods__:
raise TypeError("{} has not implemented abstract methods {}".format(
cls.__name__, ", ".join(cls.__abstractmethods__)))
class Foo(metaclass=LoggerMeta):
@abc.abstractmethod
def foo(self):
pass
class FooImpl(Foo):
def a(self):
pass
v = FooImpl()
v.foo()
当我运行它时,它会打印执行的foo。但是我预计它会失败,因为我没有在FooImpl
中实现foo
如何修复此问题?问题在于,当您修饰一个函数(或方法)并返回另一个对象时,实际上是用其他对象替换了该函数(方法)。在您的例子中,该方法不再是
abstractmethod
。它是一个包装抽象方法的函数,该方法不被ABCMeta
识别为抽象
在这种情况下,修复相对容易:
但是,您不再需要LoggerMeta.\uuuu init\uuuu
。当存在未实现的抽象方法时,您可以简单地让ABCMeta
处理这种情况。如果没有LoggerMeta.\uuuu init\uuuu
方法,这将引发另一个异常:
TypeError: Can't instantiate abstract class FooImpl with abstract methods foo
functools.wrapps
不仅能正确处理抽象方法。它还保留了修饰函数的签名和文档(以及其他一些好东西)。如果您使用decorator来简单地包装函数,那么您几乎总是希望使用functools.wrapps
嗯。。。您在元类\uuuu new\uuuu
中用非抽象函数修饰(替换)了abstractmethod。你期望什么?你只能装饰非抽象的成员。这应该行得通。我也认为是这样的。但我希望抽象成员也被记录下来。我想我不熟悉ABCMeta是如何实现的。我认为只要我们不改变函数名,当涉及到抽象方法检查时,它指向哪个实际函数就不重要了。
TypeError: Foo has not implemented abstract methods foo
TypeError: Can't instantiate abstract class FooImpl with abstract methods foo