8.14 實現(xiàn)自定義容器

2018-02-24 15:26 更新

問題

你想實現(xiàn)一個自定義的類來模擬內(nèi)置的容器類功能,比如列表和字典。但是你不確定到底要實現(xiàn)哪些方法。

解決方案

collections 定義了很多抽象基類,當(dāng)你想自定義容器類的時候它們會非常有用。比如你想讓你的類支持迭代,那就讓你的類繼承 collections.Iterable 即可:

import collections
class A(collections.Iterable):
    pass

不過你需要實現(xiàn) collections.Iterable 所有的抽象方法,否則會報錯:

>>> a = A()
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class A with abstract methods __iter__
>>>

你只要實現(xiàn) __iter__() 方法就不會報錯了(參考4.2和4.7小節(jié))。

你可以先試著去實例化一個對象,在錯誤提示中可以找到需要實現(xiàn)哪些方法:

>>> import collections
>>> collections.Sequence()
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Sequence with abstract methods \
__getitem__, __len__
>>>

下面是一個簡單扼示例,繼承自上面Sequence抽象類,并且實現(xiàn)元素按照順序存儲:

class SortedItems(collections.Sequence):
    def __init__(self, initial=None):
        self._items = sorted(initial) if initial is not None else []

    # Required sequence methods
    def __getitem__(self, index):
        return self._items[index]

    def __len__(self):
        return len(self._items)

    # Method for adding an item in the right location
    def add(self, item):
        bisect.insort(self._items, item)

items = SortedItems([5, 1, 3])
print(list(items))
print(items[0], items[-1])
items.add(2)
print(list(items))

可以看到,SortedItems跟普通的序列沒什么兩樣,支持所有常用操作,包括索引、迭代、包含判斷,甚至是切片操作。

這里面使用到了 bisect 模塊,它是一個在排序列表中插入元素的高效方式。可以保證元素插入后還保持順序。

討論

使用 collections 中的抽象基類可以確保你自定義的容器實現(xiàn)了所有必要的方法。并且還能簡化類型檢查。你的自定義容器會滿足大部分類型檢查需要,如下所示:

>>> items = SortedItems()
>>> import collections
>>> isinstance(items, collections.Iterable)
True
>>> isinstance(items, collections.Sequence)
True
>>> isinstance(items, collections.Container)
True
>>> isinstance(items, collections.Sized)
True
>>> isinstance(items, collections.Mapping)
False
>>>

collections 中很多抽象類會為一些常見容器操作提供默認的實現(xiàn),這樣一來你只需要實現(xiàn)那些你最感興趣的方法即可。假設(shè)你的類繼承自 collections.MutableSequence ,如下:

class Items(collections.MutableSequence):
    def __init__(self, initial=None):
        self._items = list(initial) if initial is not None else []

    # Required sequence methods
    def __getitem__(self, index):
        print('Getting:', index)
        return self._items[index]

    def __setitem__(self, index, value):
        print('Setting:', index, value)
        self._items[index] = value

    def __delitem__(self, index):
        print('Deleting:', index)
        del self._items[index]

    def insert(self, index, value):
        print('Inserting:', index, value)
        self._items.insert(index, value)

    def __len__(self):
        print('Len')
        return len(self._items)

如果你創(chuàng)建 Items 的實例,你會發(fā)現(xiàn)它支持幾乎所有的核心列表方法(如append()、remove()、count()等)。下面是使用演示:

>>> a = Items([1, 2, 3])
>>> len(a)
Len
3
>>> a.append(4)
Len
Inserting: 3 4
>>> a.append(2)
Len
Inserting: 4 2
>>> a.count(2)
Getting: 0
Getting: 1
Getting: 2
Getting: 3
Getting: 4
Getting: 5
2
>>> a.remove(3)
Getting: 0
Getting: 1
Getting: 2
Deleting: 2
>>>

本小節(jié)只是對Python抽象類功能的拋磚引玉。numbers 模塊提供了一個類似的跟整數(shù)類型相關(guān)的抽象類型集合。可以參考8.12小節(jié)來構(gòu)造更多自定義抽象基類。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號