Python中的abc模塊


前言
在《抽象基類(ABC)》中,基於C++講述抽象基類。盡管Python設計上以鴨子類型為主,但仍有抽象基類(ABC)的一席之地,它被封裝在了abc模塊中供程序員使用。

abc模塊有以下兩個主要功能:

某種情況下,判定某個對象的類型,如:isinstance(a, Sized)
強制子類必須實現某些方法,即ABC類的派生類

判斷類型
當我們判斷一個對象是否存在某個方法時,可以使用內置方法hasattr()。

class A(object):
def __len__(self):
pass

if __name__ == "__main__":
a = A()
print("存在__len__方法" if hasattr(a, "__len__") else "沒有__len__方法")

# 輸出:
存在__len__方法

但在Python中,使用hasattr()並非優雅解法,這里建議isinstance()。

abc模塊中定義了Sized類,利用Sized可以判斷一個對象里是否存在__len__方法,即:可否對這個對象使用len()函數。

from collections.abc import Sized

class A(object):
def __len__(self):
pass

if __name__ == "__main__":
a = A()
print("存在__len__方法" if isinstance(a, Sized) else "沒有__len__方法")

# 輸出:
存在__len__方法

isinstance實現原理
讓我們看看Sized類的源碼:

class Sized(metaclass=ABCMeta):

__slots__ = ()

@abstractmethod
def __len__(self):
return 0

@classmethod
def __subclasshook__(cls, C):
if cls is Sized:
return _check_methods(C, "__len__")
return NotImplemented

Sized類改寫了__subclasshook__魔法方法,使其可以通過isinstance()判斷對象是否含有__len__方法。同時,這個類必須基於元類abc.ABCMeta。我們也可以依葫蘆畫瓢,實現一個用來判斷對象是否存在greet()函數的類,盡管並不嚴謹:

import abc

class A(metaclass=abc.ABCMeta):
@classmethod
def __subclasscheck__(cls, subclass):
# 存在greet()返回True,不存在返回False
if hasattr(subclass, "greet"):
return True
return False

class B(object):
def greet(self): # 定義了greet()方法
pass

class C(object): # 沒有greet()方法
pass

class D(B): # 繼承自B類,因此繼承了greet()方法
pass

if __name__ == "__main__":
b = B()
c = C()
d = D()

print(isinstance(b, A)) # True
print(isinstance(c, A)) # False
print(isinstance(d, A)) # True

注意,此時A類可以被實例化,因為它還不是抽象基類。


實現ABC類
C++中利用純虛函數實現抽象基類,Python中寫法如下:

import abc

class A(metaclass=abc.ABCMeta):
# 利用裝飾器修飾greet()
@abc.abstractmethod
def greet(self):
print("hell world")

if __name__ == "__main__":
a = A()

解釋器如期拋錯:

TypeError: Can't instantiate abstract class A with abstract methods greet
1
這是因為A類現在就是一個抽象基類了,不可以被實例化,同時,它的子類還必須實現greet()方法,否則實例化子類時解釋器也要報錯:

import abc

class A(metaclass=abc.ABCMeta):
@abc.abstractmethod
def greet(self):
pass

class B(A):
def greet(self):
pass

class C(A):
pass

if __name__ == "__main__":
b = B() # 正常實例化
c = C() # 解釋器拋錯

# 輸出:
# C類中沒有定義greet()方法導致的報錯
Traceback (most recent call last):
File "xxx", line xxx, in <module>
c = C()
TypeError: Can't instantiate abstract class C with abstract methods greet

其他基類
abc模塊中還有實現了其他抽象基類,可以用來判斷類型或是繼承方法,這里不做詳述了:

__all__ = ["Awaitable", "Coroutine",
"AsyncIterable", "AsyncIterator", "AsyncGenerator",
"Hashable", "Iterable", "Iterator", "Generator", "Reversible",
"Sized", "Container", "Callable", "Collection",
"Set", "MutableSet",
"Mapping", "MutableMapping",
"MappingView", "KeysView", "ItemsView", "ValuesView",
"Sequence", "MutableSequence",
"ByteString",
]

文件所在路徑:…lib\_collocetions_abc.py


總結
abc模塊中定義的類兼顧了繼承抽象基類與鴨子類型的設計方式。你既可以通過繼承Sized來擁有__len__方法,此時instance(對象, Sized)返回True;也可以在自己設計的類中實現__len__,instance(對象, Sized)仍然返回True。

對抽象基類來說,需要用到裝飾器 @abc.abstractmethod;對於鴨子類型來說,需要重寫__subclasshook__魔法方法。

以上都是基於元類abc.ABCMeta實現的。順便提醒,注意import abc與from collections import abc各自的區別。
————————————————
版權聲明:本文為CSDN博主「有關心情」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_41359051/article/details/86764867


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM