抽象基類 abc
- 1. 前言
- 2. 内容
-
- 2.1 類
-
- class abc.ABC
- class abc.ABCMeta
- 2.2 注冊虛拟子類方法
-
- register(subclass)
- __subclasshook__(subclass)
- 2.3 裝飾器(其實也是方法)
-
- @abc.abstractmethod
- [email protected]~~
- [email protected]~~
- [email protected]~~
- 2.4 其他方法
-
- abc.get_cache_token()
源碼: Lib/abc.py
1. 前言
該子產品提供了用于在Python中定義抽象基類(ABC)的基礎結構,如 PEP 3119 中所述; 請參閱PEP,了解為何将其添加到Python中。(也可以看看PEP 3141和 numbers子產品關于基于ABC的數字類型層次結構的 。)
collections子產品有一些來自ABCs的具體類; 當然,這些可以進一步推導出來。此外, collections.abc子子產品還有一些ABCs可用于測試類或執行個體是否提供特定接口,例如,如果是否可哈希,或者它是否是一個映射。
2. 内容
該子產品提供了
元類 ABCMeta用于定義ABCs 和一個輔助類ABC,以通過繼承來定義ABCs:
2.1 類
class abc.ABC
一個輔助類,具有ABCMeta作為其元類。可以通過簡單地從ABC建立抽象基類, 避免有時混淆元類使用的方法,例如:
from abc import ABC
class MyABC(ABC):
pass
請注意,
type(ABC)
仍然是 ABCMeta,是以繼承ABC需要有關元類使用的正常預防措施,因為多重繼承可能導緻元類沖突。
譯者注:使用此種
class MyABC(ABC)
抽象基類,子類必須覆寫所有的抽象基類的抽象方法和抽象屬性,否則會報錯,如下面的例子。
# 1.直接使用抽象基類
class MyABC(ABC):
pass
@abstractmethod
def get_name(self):
pass
class Stu(MyABC):
pass
def __init__(self,name):
self.name = name
def __str__(self):
return self.name
print(type(Stu)) # <class 'abc.ABCMeta'>
s = Stu('leng')
# 結果
<class 'abc.ABCMeta'>
Traceback (most recent call last):
File "D:/pyt-code/python/mod/abc1.py", line 18, in <module>
s = Stu('leng')
TypeError: Can't instantiate abstract class Stu with abstract methods get_name
class abc.ABCMeta
用于定義抽象基類(ABCs)的元類。
通過傳遞
metaclass=ABCMeta
來定義抽象基類,例如:
from abc import ABCMeta
class MyABC(metaclass=ABCMeta):
pass
版本3.4中的新功能。
使用此元類建立ABC。ABC可以直接子類化,然後充當混合類。您還可以将不相關的具體類(甚至是内置類)和不相關的ABC注冊為“虛拟子類” - 這些将被内置函數issubclass() 視為ABC注冊的子類,但注冊ABC将不會顯示在他們的MRO(方法解析順序)中,注冊ABC定義的方法實作也不可調用(甚至通過 super()也不可以)。
2.2 注冊虛拟子類方法
使用元類ABCMeta建立的類具有以下方法:
register(subclass)
将子類注冊為此ABC的“虛拟子類”。例如:
from abc import abstractmethod,ABCMeta
# 1. 虛拟子類 通過register
class MyABC1(metaclass=ABCMeta):
pass
@abstractmethod
def get_name(self):
pass
class Stu1:
pass
def __init__(self,name):
self.name = name
def __str__(self):
return self.name
MyABC1.register(Stu1)
a = Stu1("leng")
print(a,issubclass(Stu1,ABCMeta),issubclass(Stu1,MyABC1),type(MyABC1))
# 輸出結果
leng False True <class 'abc.ABCMeta'>
版本3.3中更改:傳回已注冊的子類,以允許用作類裝飾器。
在版本3.4中更改:要檢測調用register(),您可以使用 get_cache_token()功能。
譯者注:這裡的ABC的抽象方法
get_name()
都可以不用實作。
__subclasshook__(subclass)
可以在抽象基類中覆寫此方法↑(必須定義為類方法。)
檢查子類是否被視為此ABC的子類。這意味着您可以進一步自定義
issubclass
的行為,而無需調用register()并且要考慮每個ABC子類。(這個類方法是被ABC中的
__subclasscheck__()
調用的。)
這個方法應該傳回
True
,
False
或者
NotImplemented
。如果它傳回True,則子類被視為該ABC的子類。如果它傳回False,則子類不被視為該ABC的子類,即使它通常是一個它的子類。如果它傳回
NotImplemented
,則使用通常的機制繼續子類檢查。
有關這些概念的示範,請檢視此示例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__()
定義為抽象方法。這裡給出的實作仍然可以從子類調用。
get_iterator()
方法也是抽象基類MyIterable的一部分,但不必在非抽象派生類中重寫。
這裡定義的
__subclasshook__()
類方法表示任何在其
__dict__
(或其基類之一,通過
__mro__
清單通路)中具有
__iter__()
方法的類都被被認為是一個
MyIterable
類。
最後,最後一行将Foo注冊成了一個MyIterable的虛拟子類,即使它沒有定義一個
__iter__()
方法(它使用舊的可疊代協定,隻要定義了
__len__()
和
__getitem__()
)。請注意,父類的get_iterator方法在Foo中不可見,是以它是單獨提供的。
2.3 裝飾器(其實也是方法)
abc子產品還提供以下裝飾器:
@abc.abstractmethod
訓示某方法是抽象方法的裝飾器。
使用這個裝飾器要求類的元類是ABCMeta 或從它派生的。除非所有抽象方法和屬性都被覆寫,否則無法執行個體化具有派生自元類ABCMeta的類 。可以使用任何正常的“super”調用機制來調用抽象方法。 abstractmethod()可用于聲明抽象方法的屬性和描述符。
不支援動态地将抽象方法添加到類中,或者嘗試在建立方法或類時修改其抽象狀态。abstractmethod僅影響使用正常繼承派生的子類; 使用ABC register()方法注冊的“虛拟子類” 不受影響。
當abstractmethod與其他方法描述符結合使用時,它應該作為最裡面的裝飾器應用,如以下用法示例所示:
class C(ABC):
@abstractmethod
def my_abstract_method(self, ...):
...
@classmethod
@abstractmethod
def my_abstract_classmethod(cls, ...):
...
@staticmethod
@abstractmethod
def my_abstract_staticmethod(...):
...
@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)
為了正确地與抽象基類機制進行互操作,描述符必須使用
__isabstractmethod__
将自身辨別為抽象的。通常,如果用于組成描述符的任何方法都是抽象的,此屬性應該是True 。例如,Python的内置property相當于:
class Descriptor:
...
@property
def __isabstractmethod__(self):
return any(getattr(f, '__isabstractmethod__', False) for
f in (self._fget, self._fset, self._fdel))
注意
與Java抽象方法不同,這些抽象方法可能具有實作。如果想實作,可以通過super()調用此方法并做實作。這可以作為使用協作多重繼承的架構中的超級調用的終點。
該abc子產品還支援以下傳統裝飾器:
@abc.abstractclassmethod
版本3.2中的新功能。
自從3.3版本不推薦使用:現在可以使用classmethod和 abstractmethod,使這個裝飾是多餘的。
class C(ABC):
@classmethod
@abstractmethod
def my_abstract_classmethod(cls, ...):
...
它是内置classmethod()的子類,表示抽象類方法。類似于abstractmethod()。
@abc.abstractstaticmethod
版本3.2中的新功能。
自從3.3版本不推薦使用:現在可以使用staticmethod用 abstractmethod,使這個裝飾是多餘的。
class C(ABC):
@staticmethod
@abstractmethod
def my_abstract_staticmethod(...):
...
内置staticmethod()的子類,表示抽象的staticmethod。否則它類似于abstractmethod()。
@abc.abstractproperty
自從3.3版本不推薦使用:現在可以使用
property,property.getter(), property.setter(),property.deleter()
和abstractmethod()組合,使這個裝飾是多餘的。
内置property()的子類,表示抽象屬性。
class C(ABC):
@property
@abstractmethod
def my_abstract_property(self):
...
上面的示例定義了一個隻讀屬性; 您還可以通過将一個或多個基礎方法适當地标記為abstract來定義讀寫抽象屬性:
class C(ABC):
@property
def x(self):
...
@x.setter
@abstractmethod
def x(self, val):
...
如果隻有一些元件是抽象的,那麼隻需要更新那些元件以在子類中建立具體屬性:
class D(C):
@C.x.setter
def x(self, val):
...
2.4 其他方法
abc.get_cache_token()
傳回目前的抽象基類緩存标記。
令牌是一個不透明對象(支援相等性測試),用于辨別虛拟子類的抽象基類高速緩存的目前版本。令牌随着ABCMeta.register()對任何ABC的每次調用而改變。
版本3.4中的新功能。