【问题标题】:How to tell if a class is abstract in Python 3?如何判断一个类在 Python 3 中是否是抽象的?
【发布时间】:2016-08-01 20:12:56
【问题描述】:

我编写了一个元类,它在运行时自动将其类注册到字典中。为了使其正常工作,它必须能够忽略抽象类。

代码在 Python 2 中运行良好,但我在尝试使其与 Python 3 兼容时遇到了困难。

这是当前代码的样子:

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

不过,我不知道如何在 Python 3 中实现这一点。

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

【问题讨论】:

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

标签: python metaclass six


【解决方案1】:

PEP3119 描述了 ABCMeta 元类如何“标记”抽象方法并创建一个 __abstractmethods__frozenset,其中包含一个类的所有仍然是抽象的方法。因此,要检查 cls 是否是抽象类,请检查 cls.__abstractmethods__ 是否为空。

我还发现this relevant post on abstract classes 很有用。

【讨论】:

    【解决方案2】:

    当我发布这个我正在处理XY Problem 的问题时,我无法摆脱这种感觉。事实证明,that's exactly what was going on

    这里真正的问题是AutoRegister 元类在实现时依赖于对抽象类是什么的错误理解。不管是不是 Python,抽象类最重要的标准之一是 it is not instanciable

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

    我们这里不就是分叉兔子吗?

    好吧,为什么我很难让AutoRegister 在 Python 3 中工作?因为我试图构建一些行为与 Python 中类的工作方式相矛盾的东西。

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

    那么真正的解决方案是什么?

    首先,我们必须认识到BaseWidgetBaseGizmo 没有理由存在。 它们没有提供足够的可实例化功能,也没有声明描述功能的抽象方法他们失踪了。

    有人可能会争辩说它们可以用来“分类”他们的子类,但是 a) 这显然不是在这种情况下发生的事情,并且 b)quack

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

    1. 修改 BaseWidgetBaseGizmo 以便它们定义一个或多个抽象方法。

      • 如果我们不能提出任何抽象方法,那么我们可以完全删除它们吗?
      • 如果我们无法删除它们,但也无法将它们正确抽象化,那么可能值得退后一步,看看是否有其他方法可以解决这个问题。
    2. 修改AutoRegister的定义,使其使用inspect.isabstract来判断一个类是否为抽象类:see final implementation

    这很酷,但是如果我不能更改基类怎么办?

    或者,如果您必须保持与现有代码的向后兼容性(就像我一样),装饰器可能更容易:

    @widget_registry.register
    class AlphaWidget(object):
        pass
    
    @widget_registry.register
    class BravoGizmo(object):
        pass
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-03-01
      • 2016-06-17
      • 1970-01-01
      • 2016-01-25
      • 2011-04-25
      • 2019-02-11
      • 2012-10-16
      相关资源
      最近更新 更多