Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/339.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 在修饰ABCMeta子类中的所有方法时强制执行abstractmethod行为_Python_Python 3.x_Metaprogramming_Metaclass_Abstract Methods - Fatal编程技术网

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