如何判断 class 在 Python 3 中是否是抽象的?
How to tell if a class is abstract in Python 3?
我写了一个 metaclass,它会在 运行 时自动在字典中注册它的 classes。为了使其正常工作,它必须能够忽略抽象 classes.
该代码在 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:
metaclass = attrs.get('__metaclass__')
if not (metaclass and issubclass(metaclass, ABCMeta)):
registry.register(self)
return _metaclass
Python2 中的用法如下所示:
# 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.
中完成这项工作
metaclass 如何确定它是否正在初始化 Python 3 中的摘要 class?
当我发布这个问题时,我无法摆脱我正在处理 XY Problem. As it turns out, .
的感觉
这里真正的问题是 AutoRegister
元 class 的实现依赖于对抽象 class 是什么的错误理解。 Python 与否,摘要 class 最重要的标准之一是 it is not instanciable.
在问题中发布的示例中,BaseWidget
和 BaseGizmo
是可实例化的,因此它们不是抽象的。
我们这里不就是分叉兔子吗?
嗯,为什么我在 AutoRegister
中工作时遇到这么多麻烦 Python 3?因为我试图构建一些行为与 classes 在 Python.
中的工作方式相矛盾的东西
inspect.isabstract
没有返回我想要的结果这一事实应该是一个主要的危险信号:AutoRegister
是保修无效者。
那么真正的解决方案是什么?
首先,我们必须认识到 BaseWidget
和 BaseGizmo
没有理由存在。 它们没有提供足够的功能来实例化,也没有他们是否声明了描述他们所缺少的功能的抽象方法。
有人可能会争辩说他们可以习惯于 "categorize" 他们的子 classes,但是 a) 这显然不是这种情况下发生的事情,b) quack .
相反,我们可以接受 Python 对 "abstract" 的定义:
修改BaseWidget
和BaseGizmo
,使它们定义一个或多个抽象方法。
- 如果我们不能想出任何抽象方法,那么我们可以完全删除它们吗?
- 如果我们既不能删除它们又不能使它们适当地抽象化,可能值得退后一步,看看是否有其他方法可以解决这个问题。
修改AutoRegister
的定义,使其使用inspect.isabstract
来判断class是否是抽象的:see final implementation.
这很酷,但是如果我无法更改基 classes 怎么办?
或者,如果您必须保持与现有代码的向后兼容性(就像我的情况一样),装饰器可能更容易:
@widget_registry.register
class AlphaWidget(object):
pass
@widget_registry.register
class BravoGizmo(object):
pass
PEP3119 描述了 ABCMeta metaclass "marks" 抽象方法如何创建一个 __abstractmethods__
frozenset,其中包含 class 的所有方法抽象的。因此,要检查 class cls
是否抽象,请检查 cls.__abstractmethods__
是否为空。
我还发现 this relevant post on abstract classes 有用。
我写了一个 metaclass,它会在 运行 时自动在字典中注册它的 classes。为了使其正常工作,它必须能够忽略抽象 classes.
该代码在 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:
metaclass = attrs.get('__metaclass__')
if not (metaclass and issubclass(metaclass, ABCMeta)):
registry.register(self)
return _metaclass
Python2 中的用法如下所示:
# 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.
中完成这项工作metaclass 如何确定它是否正在初始化 Python 3 中的摘要 class?
当我发布这个问题时,我无法摆脱我正在处理 XY Problem. As it turns out,
这里真正的问题是 AutoRegister
元 class 的实现依赖于对抽象 class 是什么的错误理解。 Python 与否,摘要 class 最重要的标准之一是 it is not instanciable.
在问题中发布的示例中,BaseWidget
和 BaseGizmo
是可实例化的,因此它们不是抽象的。
我们这里不就是分叉兔子吗?
嗯,为什么我在 AutoRegister
中工作时遇到这么多麻烦 Python 3?因为我试图构建一些行为与 classes 在 Python.
inspect.isabstract
没有返回我想要的结果这一事实应该是一个主要的危险信号:AutoRegister
是保修无效者。
那么真正的解决方案是什么?
首先,我们必须认识到 BaseWidget
和 BaseGizmo
没有理由存在。 它们没有提供足够的功能来实例化,也没有他们是否声明了描述他们所缺少的功能的抽象方法。
有人可能会争辩说他们可以习惯于 "categorize" 他们的子 classes,但是 a) 这显然不是这种情况下发生的事情,b) quack .
相反,我们可以接受 Python 对 "abstract" 的定义:
修改
BaseWidget
和BaseGizmo
,使它们定义一个或多个抽象方法。- 如果我们不能想出任何抽象方法,那么我们可以完全删除它们吗?
- 如果我们既不能删除它们又不能使它们适当地抽象化,可能值得退后一步,看看是否有其他方法可以解决这个问题。
修改
AutoRegister
的定义,使其使用inspect.isabstract
来判断class是否是抽象的:see final implementation.
这很酷,但是如果我无法更改基 classes 怎么办?
或者,如果您必须保持与现有代码的向后兼容性(就像我的情况一样),装饰器可能更容易:
@widget_registry.register
class AlphaWidget(object):
pass
@widget_registry.register
class BravoGizmo(object):
pass
PEP3119 描述了 ABCMeta metaclass "marks" 抽象方法如何创建一个 __abstractmethods__
frozenset,其中包含 class 的所有方法抽象的。因此,要检查 class cls
是否抽象,请检查 cls.__abstractmethods__
是否为空。
我还发现 this relevant post on abstract classes 有用。