前言
在《抽象基類(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
