天天看點

Python 3.7.1 子產品 abc 抽象基類1. 前言2. 内容

抽象基類 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中的新功能。