解析python列表索引操作、切片操作的原理

列表元素的獲取可以使用索引或者切片,這個時候調用的是__getitem__方法

a[i] # 這是一個簡單的數字索引,使用即元素“位置”獲取元素值
a[i:j];a[i:j:k] # 這是切片操作,i爲起始位置,j爲結束位置,k爲步進值

當使用賦值表達式對元素進行賦值時,調用的時__setitem__方法

a[0]=10
#也可以使用這樣的方法替換多個元素
a[0,2]=[11,12]

當使用del語句刪除一個值時,調用的是__delitem__方法

del a[0]
del a[0,2]

當使用類似於a[:],a[1:2],a[1,10,2]這樣的操作時,實際上是在寫slice表達式。slice表達式必須要有三個參數,即起始,結束和步進。之所以我們可以少寫參數,是因爲內部使用了 indices 這個方法對參數進行了補全。

class Explore(list):
    def __getitem__(self, item):
        print(item,item.indices(len(self)))
        return super().__getitem__(item)
>> a = Explore('1234')
>> a[:]
>> slice(None, None, None) (0, 4, 1) # 雖然我們傳入的表達式僅爲一個:,但是indices進行了補全
   ['1', '2', '3', '4']

當我們要使用繼承的方式實現自己的list,並且對其中的方法和操作進行自己的一些定義,那麼一定不要忘記slice。下面是一個支持平均數和標準差方法的list。

class StatsList(list):
    def __init__(self, *args, **kwargs):
        self.sum0 = 0  # 列表中元素的個數,len(self)
        self.sum1 = 0  # 列表中所有元素的和, sum(self)
        self.sum2 = 0  # 列表中所有元素的平方和, sum(x**2 for x in self)
        super().__init__(*args, **kwargs)
        for x in self:
            self._new(x)

    # 當添入元素時調用
    def _new(self, value):
        self.sum0 += 1
        self.sum1 += value
        self.sum2 += value * value

    # 當移除元素時調用
    def _rmv(self, value):
        self.sum0 -= 1
        self.sum1 -= value
        self.sum2 -= value * value

    def insert(self, index, value):
        super().insert(index, value)
        self._new(value)

    def pop(self, index=-1):
        value = super().pop(index)
        self._rmv(value)

    def __setitem__(self, key, value):
        # 如果key是slice實例,那麼在進行切片賦值
        if isinstance(key, slice):
            start, stop, step = key.indices(self.sum0)
            for i in range(start, stop, step):
            	self._rmv(self[i])
            super().__setitem__(key, value)
            # 這裏value不存在不可迭代的情況,因爲 slice 操作參數必須爲可迭代對象
            for x in value:
                self._new(x)
        else:
            self._rmv(self[key])
            self._new(value)
            super().__setitem__(key, value)

    def __delitem__(self, key):
        if isinstance(key, slice):
            start, stop, step = key.indices(self.sum0)
            for i in range(start, stop, step):
            	self._rmv(self[i])
            super().__delitem__(key)
        else:
            self._rmv(self[key])
            super().__delitem__(key)

    """
    其他影響計算的函數有: extend, __iadd__, remove, append, 
    """

    # 平均數
    @property
    def mean(self):
        return self.sum1 / self.sum0

    # 標準差
    @property
    def stdev(self):
        return math.sqrt(self.sum0 * self.sum2 - self.sum1 ** 2) / self.sum0
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章