Python介面與抽象類別之設計:abc模組(Abstract Base Classes)

Python中介面抽象類別之設計:abc模組(Abstract Base Classes)

介面作法如下:

抽象類別作法如下:

01. Quickstart Tutorial

使用 Python abc 模組的原因是為了要解決 Python 沒有「抽象類別 (abstract class)」的問題。透過抽象類別,我們可以建立一個比使用 hasattr() 還要更嚴格的類別介面 (class interface) 檢查。舉例而言,當我們建立一個動物的類別,我們希望之後繼承實作的類別都一定要有screaming」以及「walk」的方法,我們可以透過 abc.ABCMeta 這個 metaclass 來定義 Animal 抽象類別:

import abc     #注意:此非採用 fromabc import ABCMeta,abstractmethod 方式

 

class Animal(metaclass=abc.ABCMeta):

    @abc.abstractmethod

    def screaming(self):

        'Return when animal screaming the sound hear likes'

        raise NotImplemented

 

    @abc.abstractmethod

    def walk(self, x, y):

        'Make animal walk to position (x, y).'

        raise NotImplemented

如果不透過 metaclass指定方式,我們也可以透過繼承 abc.ABC 這個 helper class 達到相同的效果:

import abc

 

class Animal(abc.ABC):

    @abc.abstractmethod

    def screaming(self):

        'Return when animal screaming the sound hear likes'

        raise NotImplemented

 

    @abc.abstractmethod

    def walk(self, x, y):

        'Make animal walk to position (x, y).'

        raise NotImplemented

當我們繼承使用 Animal 這個抽象類別來建立類別時,就必須要實作 screaming 以及 walk 如果沒有實作的話,Python 就會產生 TypeError

>>> class Dog(Animal):

...    pass

...

>>> Dog()         # Create a instance(注:Python實例化物件不使用new關鍵字)

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

TypeError: Can't instantiate abstract class Dog with abstract methods screaming, walk

我們現在來實作 Dog 這個類別:

>>> class Dog(Animal):

...     x = 0

...     y = 0

...     def screaming(self):

...         return 'Wof, Wof'

...     def walk(self, x, y):

...         self.x = x

...         self.y = y

...         return (self.x, self.y)

...

>>> Dog()           # Create a instance

<__main__.Dog object at 0x7fc207b10208>

>>> dog = Dog()

>>> dog.screaming()

'Wof, wof!'

>>> dog.walk(10, 20)

(10, 20)

我們可透過 isinstance 以及 issubclass 方法來檢查 Dog 的類別:

>>> isinstnace(Dog(), Animal)

True

>>> issubclass(Dog, Animal)

True

如果有一天,我們想要把一個Animal可視為Python built-in 的 list 类型來使用(*即[Animal]),我們可以透過 register 來改變這個事實:

>>> isinstance([], Animal)

False

>>> issubclass(list, Animal)

False

>>> Animal.register(list)      # Recognize Animal as list (*即Animal具有list功能

>>> isinstance([], Animal)

True

>>> issubclass(list, Animal)

True

這個用法在 collections.abc 被大量的使用,CPython collections.abc 中定義了許多的基礎抽象類別,例如說 SequenceIterable 等。現實中Sequence 可符合定義的類別有:tuple、str、range、memoryview。我們可以在原始碼看到他們被 Sequence 所 register

Sequence.register(tuple)

Sequence.register(str)

Sequence.register(range)

Sequence.register(memoryview)

接著,我們就可以使用 Sequence 這個抽象類別來作 list 以及 tuple 功能在這邊我們稱作為 virtual 化

>>> from collections import abc

>>> isinstance([], abc.Sequence)

True

>>> isinstnace((), abc.Sequence)

True

>>> issubclass(list, abc.Sequence)

True

>>> issubclass(tuple, abc.Sequence)

True

02. HOW-TO GUIDE

前面提到了抽象方法,那其它的類別方法靜態方法也能夠被抽象化嗎?答案是可以,自 Python 3.3 版後,只需要在 method decorator再加上 @abstractmethod 即可

  • 抽象化 classmethod 以及 staticmethod

import abc

 

class Base(abc.ABC): 

    @classmethod

    @abc.abstractmethod

    def setUpClass(cls):

        raise NotImplemented

 

    @staticmethod

    @abc.abstractmethod

    def count(self, data):

        raise NotImplemented

  

class Implementation(Base): 

    @classmethod

    def setUpClass(cls):

        cls.count = 0

 

    @staticmethod

    def count(self, data):

        self.count = len(data)

        return self.count

  • 抽象化 property

class 裡面的 property 也可以作抽象化,同樣在 decorator再加上 @abstractmethod 即可:

import abc

  

class Base(abc.ABC):

    _index = 0

 

    @property     

    @abc.abstractmethod

    def index(self):

        raise NotImplemented

 

    @index.setter

    @abc.abstractmethod

    def index(self, new_index):

        raise NotImplemented

  

class Implementation(Base):

    MAX_LEN = 100

 

    @property

    def index(self):

        return self._index

 

    @index.setter

    def index(self, new_index):

        new_index = min(new_index, self.MAX_LEN)

        self._index = new_index

 

imp = Implementation()

print(imp.index)

imp.index = 50

print(imp.index)

imp.index = 500

print(imp.index)

03. DISCUSSIONS

> abc 的運作原理?

abc.ABCMeta 主要可做到三件事情:

ABCMeta metaclass,因此__new__ 方法內會作 abstract 檢查的部分

  1.     override __isinstancecheck__
  2.     override __issubclasscheck__

 

> abc.ABC helper class 的運作原理?

abc.ABC 程式碼如下:

class ABC(metaclass=ABCMeta):

    """Helper class that provides a standard way to create an ABC using

    inheritance.

    """

    __slots__ = ()

透過 metaclass=ABCMeta 來指定metaclass此可讓一般類別繼承 ABC時即具有 ABCMeta 的功能。接者 __slots__ 設為 (),這樣的設定讓其他人無法修改此 ABC 的 attributes

>>> import abc

>>> class Foo(object):

...     pass

...

>>> foo = Foo()

>>> foo.bar = 10     #

>>> print(foo.bar)

10

>>> a = abc.ABC()

>>> a.bar = 10    #不可

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

AttributeError: 'ABC' object has no attribute 'bar'

有關 __slots__ 的介紹,請參考 __slots__ 以及 使用 __slots__

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值