1. 程式人生 > >Python資料之abc模組

Python資料之abc模組

python中並沒有提供抽象類與抽象方法,但是提供了內建模組abc(abstract base class)來模擬實現抽象類。

關於Python抽象類的一些說明:
    java中的抽象類是為了實現c++中的抽象類和模板之類的東西介面是為了解決java不能多繼承的問題。然而,java的“介面”是一個特例而非普通現象。如果可以多繼承的話,其實並不需要介面。

python才比較符合現實邏輯的“面向物件”:
    python允許多繼承,正如現實中,你既是公民也是納稅人,我們直接使用這些“類”而不需要特別的建立什麼“納稅人介面”
    python中所有的類,都是抽象類,或者說根本不存在抽象類,類方法可以直接使用,“類”本身在定義的時候就已經例項化,你可以通過輸入:某類[回車]看到其記憶體控制代碼。這是符合事實的,並且時簡約明瞭的。而在C++和java當中,一個類定義了以後,肯定是佔用了記憶體空間,但是同時他又沒有例項化,如果要使用的話還得例項化一次,又要佔用一些記憶體空間。而類定義所佔用的記憶體空間,使用率是很低的。

1、abc模組的組成:

1)abc.ABCMeta 這是用來生成抽象基礎類的元類。由它生成的類可以被直接繼承。

2)abc.ABC輔助類,讓你可以不用關心元類概念,直接繼承它,就有了ABCMeta元類。使用時注意元類衝突

3)@abc.abstractmethod 定義抽象方法,通過@abc.abstractmethod將方法宣告為抽象方法。除了這個裝飾器,其餘裝飾器都被deprecated了。

簡單示例:

from abc import ABCMeta

class MyABC(metaclass=ABCMeta):
    pass

MyABC.register(tuple
)
assert issubclass(tuple, MyABC) assert isinstance((), MyABC)

首先生成了一個MyABC的抽象基礎類,然後再將tuple變成它的虛擬子類。然後通過issubclass或者isinstance都可以判斷出tuple是不是出於MyABC類。



2、具體化抽象類:
具體化抽象類可以有兩種方式,一種通過註冊(register),另外一種通過繼承。

註冊方式的缺點:不會出現在類的MRO (Method Resolution Order),故而也不能通過super()來呼叫抽象方法。當沒有實現抽象方法時,例項化時候不會報錯,只有在呼叫時候才會報錯。

繼承方式的優點:直接從抽象基類派生子類有一個好處,除非子類實現抽象基類的抽象方法,否則子類不能例項化。

示例:

from abc import ABCMeta, abstractmethod, abstractproperty


#class Drawable(metaclass=ABCMeta):   #python 3.x 寫法,替代 __metaclass__ = ABCMeta

class Drawable(object):
    """docstring for Drawable"""
     __metaclass__ = ABCMeta

    #@property       #python 3.x 寫法替代@abstractproperty

    @abstractproperty
    def size(self):
        # return 'mysize'
        pass

    @abstractmethod
    def draw(self, x, y, scale=1.0):
        # print(x * scale, y * scale)
        pass

    def double_draw(self, x, y):
        self.draw(x, y, scale=2.0)


class Cicle(Drawable):
    # 1. 使用繼承的方法
    """docstring for Cicle"""

    def draw(self, x, y, scale=1.0):
        print(x * scale, y * scale)

    @property
    def size(self):
        return 'Cicle size'


# Cicle如果沒有override draw函式和size 屬性,那麼例項化的時候就會報錯
# TypeError: Can't instantiate abstract class Cicle with abstract methods draw, size
c = Cicle()
print(dir(c))
c.draw(1, 2)
c.double_draw(1, 2)
print(isinstance(c, Drawable))  # True
print(isinstance(c, Drawable))  # True


class Rectangle():
    """docstring for Cicle"""
    pass

# 使用抽象類函式的register方法註冊具體的class
# 通過註冊的類,可以直接例項化,但是無法訪問抽象類的所有成員
# 其實就是隻是讓isinstance、issubclass識別註冊的類為抽象類的成員和例項
Drawable.register(Rectangle)

r = Rectangle()
# r.double_draw(1, 2) 
# AttributeError: 'Rectangle' object has no attribute 'double_draw'
print(dir(r))
print(isinstance(r, Drawable))  # True
print(issubclass(Rectangle, Drawable))  # True

執行結果:

# ['__abstractmethods__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_abc_cache', '_abc_negative_cache', '_abc_negative_cache_version', '_abc_registry', 'double_draw', 'draw', 'size']
# 1.0 2.0
# 2.0 4.0
# True
# True
# ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
# True
# True
# [Finished in 0.2s]