abc
--- 抽象基類(lèi)?
源代碼: Lib/abc.py
該模塊提供了在 Python 中定義 抽象基類(lèi) (ABC) 的組件,在 PEP 3119 中已有概述。查看 PEP 文檔了解為什么需要在 Python 中增加這個(gè)模塊。(也可查看 PEP 3141 以及 numbers
模塊了解基于 ABC 的數字類(lèi)型繼承關(guān)系。)
collections
模塊中有一些派生自 ABC 的具體類(lèi);當然這些類(lèi)還可以進(jìn)一步被派生。此外,collections.abc
子模塊中有一些 ABC 可被用于測試一個(gè)類(lèi)或實(shí)例是否提供特定的接口,例如它是否可哈?;蛩欠駷橛成涞?。
該模塊提供了一個(gè)元類(lèi) ABCMeta
,可以用來(lái)定義抽象類(lèi),另外還提供一個(gè)工具類(lèi) ABC
,可以用它以繼承的方式定義抽象基類(lèi)。
- class abc.ABC?
一個(gè)使用
ABCMeta
作為元類(lèi)的工具類(lèi)。抽象基類(lèi)可以通過(guò)從ABC
派生來(lái)簡(jiǎn)單地創(chuàng )建,這就避免了在某些情況下會(huì )令人混淆的元類(lèi)用法,例如:from abc import ABC class MyABC(ABC): pass
注意
ABC
的類(lèi)型仍然是ABCMeta
,因此繼承ABC
仍然需要關(guān)注元類(lèi)使用中的注意事項,比如可能會(huì )導致元類(lèi)沖突的多重繼承。當然你也可以直接使用ABCMeta
作為元類(lèi)來(lái)定義抽象基類(lèi),例如:from abc import ABCMeta class MyABC(metaclass=ABCMeta): pass
3.4 新版功能.
- class abc.ABCMeta?
用于定義抽象基類(lèi)(ABC)的元類(lèi)。
使用該元類(lèi)以創(chuàng )建抽象基類(lèi)。抽象基類(lèi)可以像 mix-in 類(lèi)一樣直接被子類(lèi)繼承。你也可以將不相關(guān)的具體類(lèi)(包括內建類(lèi))和抽象基類(lèi)注冊為“抽象子類(lèi)” —— 這些類(lèi)以及它們的子類(lèi)會(huì )被內建函數
issubclass()
識別為對應的抽象基類(lèi)的子類(lèi),但是該抽象基類(lèi)不會(huì )出現在其 MRO(Method Resolution Order,方法解析順序)中,抽象基類(lèi)中實(shí)現的方法也不可調用(即使通過(guò)super()
調用也不行)。1使用
ABCMeta
作為元類(lèi)創(chuàng )建的類(lèi)含有如下方法:- register(subclass)?
將“子類(lèi)”注冊為該抽象基類(lèi)的“抽象子類(lèi)”,例如:
from abc import ABC class MyABC(ABC): pass MyABC.register(tuple) assert issubclass(tuple, MyABC) assert isinstance((), MyABC)
在 3.3 版更改: 返回注冊的子類(lèi),使其能夠作為類(lèi)裝飾器。
在 3.4 版更改: 你可以使用
get_cache_token()
函數來(lái)檢測對register()
的調用。
你也可以在虛基類(lèi)中重載這個(gè)方法。
- __subclasshook__(subclass)?
(必須定義為類(lèi)方法。)
檢查 subclass 是否是該抽象基類(lèi)的子類(lèi)。也就是說(shuō)對于那些你希望定義為該抽象基類(lèi)的子類(lèi)的類(lèi),你不用對每個(gè)類(lèi)都調用
register()
方法了,而是可以直接自定義issubclass
的行為。(這個(gè)類(lèi)方法是在抽象基類(lèi)的__subclasscheck__()
方法中調用的。)該方法必須返回
True
,False
或是NotImplemented
。如果返回True
,subclass 就會(huì )被認為是這個(gè)抽象基類(lèi)的子類(lèi)。如果返回False
,無(wú)論正常情況是否應該認為是其子類(lèi),統一視為不是。如果返回NotImplemented
,子類(lèi)檢查會(huì )按照正常機制繼續執行。
為了對這些概念做一演示,請看以下定義 ABC 的示例:
class Foo: def __getitem__(self, index): ... def __len__(self): ... def get_iterator(self): return iter(self) class MyIterable(ABC): @abstractmethod def __iter__(self): while False: yield None def get_iterator(self): return self.__iter__() @classmethod def __subclasshook__(cls, C): if cls is MyIterable: if any("__iter__" in B.__dict__ for B in C.__mro__): return True return NotImplemented MyIterable.register(Foo)
ABC
MyIterable
定義了標準的迭代方法__iter__()
作為一個(gè)抽象方法。這里給出的實(shí)現仍可在子類(lèi)中被調用。get_iterator()
方法也是MyIterable
抽象基類(lèi)的一部分,但它并非必須被非抽象派生類(lèi)所重載。這里定義的
__subclasshook__()
類(lèi)方法指明了任何在其__dict__
(或在其通過(guò)__mro__
列表訪(fǎng)問(wèn)的基類(lèi)) 中具有__iter__()
方法的類(lèi)也都會(huì )被視為MyIterable
。最后,末尾行使得
Foo
成為MyIterable
的一個(gè)虛子類(lèi),即使它沒(méi)有定義__iter__()
方法(它使用了以__len__()
和__getitem__()
術(shù)語(yǔ)定義的舊式可迭代對象協(xié)議)。 請注意這將不會(huì )使get_iterator
成為Foo
的一個(gè)可用方法,它是被另外提供的。
此外,abc
模塊還提供了這些裝飾器:
- @abc.abstractmethod?
用于聲明抽象方法的裝飾器。
使用此裝飾器要求類(lèi)的元類(lèi)是
ABCMeta
或是從該類(lèi)派生。一個(gè)具有派生自ABCMeta
的元類(lèi)的類(lèi)不可以被實(shí)例化,除非它全部的抽象方法和特征屬性均已被重載。抽象方法可通過(guò)任何普通的“super”調用機制來(lái)調用。abstractmethod()
可被用于聲明特性屬性和描述器的抽象方法。動(dòng)態(tài)地添加抽象方法到一個(gè)類(lèi),或嘗試在方法或類(lèi)被創(chuàng )建后修改其抽象狀態(tài)等操作僅在使用
update_abstractmethods()
函數時(shí)受到支持。abstractmethod()
只會(huì )影響使用常規繼承所派生的子類(lèi);通過(guò) ABC 的register()
方法注冊的“虛子類(lèi)”不會(huì )受到影響。當
abstractmethod()
與其他方法描述符配合應用時(shí),它應當被應用為最內層的裝飾器,如以下用法示例所示:class C(ABC): @abstractmethod def my_abstract_method(self, arg1): ... @classmethod @abstractmethod def my_abstract_classmethod(cls, arg2): ... @staticmethod @abstractmethod def my_abstract_staticmethod(arg3): ... @property @abstractmethod def my_abstract_property(self): ... @my_abstract_property.setter @abstractmethod def my_abstract_property(self, val): ... @abstractmethod def _get_x(self): ... @abstractmethod def _set_x(self, val): ... x = property(_get_x, _set_x)
為了能正確地與抽象基類(lèi)機制實(shí)現互操作,描述符必須使用
__isabstractmethod__
將自身標識為抽象的。 通常,如果被用于組成描述符的任何方法都是抽象的則此屬性應當為True
。 例如,Python 的內置property
所做的就等價(jià)于:class Descriptor: ... @property def __isabstractmethod__(self): return any(getattr(f, '__isabstractmethod__', False) for f in (self._fget, self._fset, self._fdel))
備注
不同于 Java 抽象方法,這些抽象方法可能具有一個(gè)實(shí)現。 這個(gè)實(shí)現可在重載它的類(lèi)上通過(guò)
super()
機制來(lái)調用。 這在使用協(xié)作多重繼承的框架中可以被用作超調用的一個(gè)端點(diǎn)。
abc
模塊還支持下列舊式裝飾器:
- @abc.abstractclassmethod?
3.2 新版功能.
3.3 版后已移除: 現在可以讓
classmethod
配合abstractmethod()
使用,使得此裝飾器變得冗余。內置
classmethod()
的子類(lèi),指明一個(gè)抽象類(lèi)方法。 在其他方面它都類(lèi)似于abstractmethod()
。這個(gè)特例已被棄用,因為現在當
classmethod()
裝飾器應用于抽象方法時(shí)它會(huì )被正確地標識為抽象的:class C(ABC): @classmethod @abstractmethod def my_abstract_classmethod(cls, arg): ...
- @abc.abstractstaticmethod?
3.2 新版功能.
3.3 版后已移除: 現在可以讓
staticmethod
配合abstractmethod()
使用,使得此裝飾器變得冗余。內置
staticmethod()
的子類(lèi),指明一個(gè)抽象靜態(tài)方法。 在其他方面它都類(lèi)似于abstractmethod()
。這個(gè)特例已被棄用,因為現在當
staticmethod()
裝飾器應用于抽象方法時(shí)它會(huì )被正確地標識為抽象的:class C(ABC): @staticmethod @abstractmethod def my_abstract_staticmethod(arg): ...
- @abc.abstractproperty?
3.3 版后已移除: 現在可以讓
property
,property.getter()
,property.setter()
和property.deleter()
配合abstractmethod()
使用,使得此裝飾器變得冗余。內置
property()
的子類(lèi),指明一個(gè)抽象特性屬性。這個(gè)特例已被棄用,因為現在當
property()
裝飾器應用于抽象方法時(shí)它會(huì )被正確地標識為抽象的:class C(ABC): @property @abstractmethod def my_abstract_property(self): ...
上面的例子定義了一個(gè)只讀特征屬性;你也可以通過(guò)適當地將一個(gè)或多個(gè)下層方法標記為抽象的來(lái)定義可讀寫(xiě)的抽象特征屬性:
class C(ABC): @property def x(self): ... @x.setter @abstractmethod def x(self, val): ...
如果只有某些組件是抽象的,則只需更新那些組件即可在子類(lèi)中創(chuàng )建具體的特征屬性:
class D(C): @C.x.setter def x(self, val): ...
abc
模塊還提供了這些函數:
- abc.get_cache_token()?
返回當前抽象基類(lèi)的緩存令牌
此令牌是一個(gè)不透明對象(支持相等性測試),用于為虛子類(lèi)標識抽象基類(lèi)緩存的當前版本。 此令牌會(huì )在任何 ABC 上每次調用
ABCMeta.register()
時(shí)發(fā)生更改。3.4 新版功能.
- abc.update_abstractmethods(cls)?
重新計算一個(gè)抽象類(lèi)的抽象狀態(tài)的函數。 如果一個(gè)類(lèi)的抽象方法在類(lèi)被創(chuàng )建后被實(shí)現或被修改則應當調用此函數。 通常,此函數應當在一個(gè)類(lèi)裝飾器內部被調用。
返回 cls,使其能夠用作類(lèi)裝飾器。
如果 cls 不是
ABCMeta
的子類(lèi),則不做任何操作。備注
此函數會(huì )假定 cls 的上級類(lèi)已經(jīng)被更新。 它不會(huì )更新任何子類(lèi)。
3.10 新版功能.
備注
- 1
C++ 程序員需要注意:Python 中虛基類(lèi)的概念和 C++ 中的并不相同。