gpt4 book ai didi

python - 在装饰 ABCMeta 子类中的所有方法时强制执行抽象方法行为

转载 作者:太空宇宙 更新时间:2023-11-04 04:38:14 24 4
gpt4 key购买 nike

我想实现一个元类来包装方法以记录附加信息。但我还需要有 abstractmethods。我试图扩展 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()

当我运行它时,它会打印Executed foo。但是我预计它会失败,因为我没有在 FooImpl 中实现 foo

我该如何解决这个问题?

最佳答案

问题在于,当您装饰一个函数(或方法)并返回一个不同的对象时,您实际上用其他东西替换了该函数(方法)。在您的情况下,该方法不再是 abstractmethod 。它是一个包装 abstractmethod 的函数,ABCMeta 无法将其识别为抽象。

在这种情况下修复相对容易:functools.wraps :

import functools  # <--- This is new

def logfunc(fn, *args, **kwargs):
@functools.wraps(fn) # <--- This is new
def fncomposite(*args, **kwargs):
rt = fn(*args, **kwargs)
print("Executed %s" % fn.__name__)
return rt
return fncomposite

这就是您需要更改的全部内容。

有了这个改变,它就正确地引发了:

TypeError: Foo has not implemented abstract methods foo

但是你不再需要 LoggerMeta.__init__ 了。当存在未实现的抽象方法时,您可以简单地让 ABCMeta 处理这种情况。如果没有 LoggerMeta.__init__ 方法,这将引发另一个异常:

TypeError: Can't instantiate abstract class FooImpl with abstract methods foo

functools.wraps 不仅可以正确处理抽象方法。它还保留了装饰函数的签名和文档(以及其他一些不错的东西)。如果您使用装饰器来简单地包装函数,您几乎总是想使用 functools.wraps!

关于python - 在装饰 ABCMeta 子类中的所有方法时强制执行抽象方法行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51185007/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com