如何判断Python 3中的类是否是抽象的?

时间:2016-08-01 20:12:56

标签: python metaclass six

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

代码在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中初始化抽象类?

2 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:0)

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

这里真正的问题是AutoRegister元类,如实现的那样,依赖于对抽象类的错误理解。 Python与否,抽象类最重要的标准之一是it is not instanciable

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

Aren我们只是在这里分叉兔子?

好吧,为什么让AutoRegister在Python 3中工作时遇到这么多麻烦?因为我试图构建一些行为与Python中的类工作方式相矛盾的东西。

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

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

首先,我们必须认识到BaseWidgetBaseGizmo 没有理由存在。它们没有提供足够的功能来实现,也没有声明抽象方法描述他们缺少的功能。

有人可能会争辩说他们可以习惯于"分类"他们的子类,但a)显然不是这种情况下发生的事情,而b)quack

相反,我们可以接受Python对#34; abstract"的定义:

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

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

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

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

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