Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/three.js/2.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
在Python3中,如何判断类是否是抽象的?_Python_Metaclass_Six - Fatal编程技术网

在Python3中,如何判断类是否是抽象的?

在Python3中,如何判断类是否是抽象的?,python,metaclass,six,Python,Metaclass,Six,我编写了一个元类,在运行时自动在dict中注册它的类。为了让它正常工作,它必须能够忽略抽象类 这段代码在Python2中运行得非常好,但我在尝试使其与Python3兼容时遇到了麻烦 下面是代码当前的样子: def AutoRegister(registry, base_type=ABCMeta): class _metaclass(base_type): def __init__(self, what, bases=None, attrs=None):

我编写了一个元类,在运行时自动在dict中注册它的类。为了让它正常工作,它必须能够忽略抽象类

这段代码在Python2中运行得非常好,但我在尝试使其与Python3兼容时遇到了麻烦

下面是代码当前的样子:

def AutoRegister(registry, base_type=ABCMeta):
    class _metaclass(base_type):
        def __init__(self, what, bases=None, attrs=None):
            super(_metaclass, self).__init__(what, bases, attrs)

            # Do not register abstract classes.
            # Note that we do not use `inspect.isabstract` here, as
            #   that only detects classes with unimplemented abstract
            #   methods - which is a valid approach, but not what we
            #   want here.
            # :see: http://stackoverflow.com/a/14410942/
            metaclass = attrs.get('__metaclass__')
            if not (metaclass and issubclass(metaclass, ABCMeta)):
                registry.register(self)

    return _metaclass
Python 2中的用法如下所示:

# Abstract classes; these are not registered.
class BaseWidget(object):  __metaclass__ = AutoRegister(widget_registry)
class BaseGizmo(BaseWidget): __metaclass__ = ABCMeta

# Concrete classes; these get registered.
class AlphaWidget(BaseWidget): pass
class BravoGizmo(BaseGizmo): pass
但是,我不知道如何在Python3中实现这一点


元类如何确定它是否在Python3中初始化抽象类?

当我发布这个问题时,我无法摆脱这种感觉,即我正在处理一个抽象类。事实证明

这里真正的问题是,
AutoRegister
元类的实现依赖于对抽象类是什么的错误理解。不管是否使用Python,抽象类最重要的标准之一就是

在问题中发布的示例中,
BaseWidget
BaseGizmo
是可实例化的,因此它们不是抽象的

我们不是把兔子分成两半吗? 那么,为什么我在让
AutoRegister
在Python3中工作时遇到这么多麻烦呢?因为我试图构建一些行为与Python中类的工作方式相矛盾的东西

inspect.isastract
没有返回我想要的结果这一事实应该是一个重大的危险信号:
AutoRegister
是一个保修无效者

那么,真正的解决办法是什么呢? 首先,我们必须认识到
BaseWidget
BaseGizmo
没有存在的理由。它们没有提供足够的可实例化功能,也没有声明描述它们缺少的功能的抽象方法

有人可能会说,它们可以用来“分类”它们的子类,但a)这显然不是本例中的情况,b)

相反,我们可以接受Python对“抽象”的定义:

  • 修改
    BaseWidget
    BaseGizmo
    ,以便它们定义一个或多个抽象方法

    • 如果我们不能想出任何抽象的方法,那么我们可以完全删除它们吗
    • 如果我们不能删除它们,但也不能使它们正确抽象,那么不妨退一步,看看是否有其他方法可以解决这个问题
  • 修改
    AutoRegister
    的定义,以便它使用
    inspect.isastract
    确定类是否为抽象类:

  • 这很酷,但是如果我不能更改基类呢? 或者,如果您必须保持与现有代码的向后兼容性(对我来说就是这样),则装饰程序可能更容易:

    @widget_registry.register
    class AlphaWidget(object):
        pass
    
    @widget_registry.register
    class BravoGizmo(object):
        pass
    
    描述ABCMeta元类如何“标记”抽象方法,并创建一个
    \uuuuu abstractmethods\uuuu
    冻结集,该冻结集包含类中仍然是抽象的所有方法。因此,要检查类
    cls
    是否是抽象的,请检查
    cls.\uuuuuAbstractMethods\uuuuuu
    是否为空


    我还发现它很有用。

    尽管使用了
    ABCMeta
    ,但在您展示的Python2代码中,您所谓的“抽象”类实际上并不是抽象的(也就是说,您可以根据需要创建它们的实例,Python不会引发异常)。对于真正抽象的东西,您需要在其中声明的某些方法上使用
    @abstractmethod
    装饰器。子类也将是抽象的,除非它们重写那些方法(不使用装饰器本身)。如果不先解决这个基本问题,我不确定你的问题是否能得到合理的答案。我感谢你的意见。你的评论促使我做了进一步的研究,现在我发现我最初对“摘要”的理解是不正确的。我得好好考虑一下;我们在代码库中经常使用这种模式,但如果我们最终以错误的方式使用工具,从长远来看,这将导致重大问题。