ABC 是一些不能被实例化的类。Java 或 C++ 语言的程序员应该对此概念十分熟悉。Python 3 添加了一个新的框架 —abc— 它提供了对 ABC 的支持。
这个 abc 模块具有一个元类(ABCMeta
)和 修饰符(@abstractmethod
和 @abstractproperty
)。如果一个 ABC 具有一个 @abstractmethod
或 @abstractproperty
,它就不能被实例化,但必须在一个子类内被覆盖。比如,如下代码:
>>>from abc import * >>>class C(metaclass = ABCMeta): pass >>>c = C()
这些代码是可以的,但是不能像下面这样编码:
>>>from abc import * >>>class C(metaclass = ABCMeta): ... @abstractmethod ... def absMethod(self): ... pass >>>c = C() Traceback (most recent call last): File "", line 1, in TypeError: Can't instantiate abstract class C with abstract methods absMethod
更好的做法是使用如下代码:
>>>class B(C): ... def absMethod(self): ... print("Now a concrete method") >>>b = B() >>>b.absMethod() Now a concrete method
ABCMeta
类覆盖属性 __instancecheck__
和 __subclasscheck__
,借此可以重载内置函数 isinstance()
和 issubclass()
。要向 ABC 添加一个虚拟子类,可以使用 ABCMeta
提供的 register()
方法。如下所示的简单示例:
>>>class TestABC(metaclass=ABCMeta): pass >>>TestABC.register(list) >>>TestABC.__instancecheck__([]) True
它等同于使用 isinstance(list, TestABC)
。您可能已经注意到 Python 3 使用 __instancecheck__
,而非 __issubclass__
,使用__subclasscheck__
,而非 __issubclass__
,这看起来更为自然。若将参数 isinstance(subclass, superclass)
反转成,比如superclass.__isinstance__(subclass)
,可能会引起混淆。可见,语法 superclass.__instancecheck__(subclass)
显然更好一点。
在 collections
模块内,可以使用几个 ABC 来测试一个类是否提供了特定的一个接口:
>>>from collections import Iterable >>>issubclass(list, Iterable) True
表 1 给出了这个集合框架的 ABC。
ABC | Inherits |
---|---|
Container |
|
Hashable |
|
Iterable |
|
Iterator |
Iterable |
Sized |
|
Callable |
|
Sequence |
Sized , Iterable , Container |
MutableSequence |
Sequence |
Set |
Sized , Iterable , Container |
MutableSet |
Set |
Mapping |
Sized , Iterable , Container |
MutableMapping |
Mapping |
MappingView |
Sized |
KeysView |
MappingView , Set |
ItemsView |
MappingView , Set |
ValuesView |
MappingView |
Python 3 现支持能代表数值类的 ABC 的类型层次结构。这些 ABC 存在于 numbers
模块内并包括 Number
、 Complex
、Real
、 Rational
和 Integral
。图 1 显示了这个数值层次结构。可以使用它们来实现您自己的数值类型或其他数值 ABC。
新模块 fractions
可实现这个数值 ABC Rational
。此模块提供对有理数算法的支持。若使用 dir(fractions.Fraction)
,就会注意到它具有一些属性,比如 imag
、 real
和 __complex__
。根据数值塔的原理分析,其原因在于 Rationals
继承自 Reals
,而 Reals
继承自 Complex
。