Fluent Python: Slice
阿新 • • 發佈:2017-10-02
des attr_ typeerror 自定義類型 attr self vector -s instance
Pyhton中序列類型支持切片功能,比如list:
>>> numbers = [1, 2, 3, 4, 5] >>> numbers[1:3] [2, 3]
tuple也是序列類型,同樣支持切片。
(一)我們是否可以使自定義類型支持切片呢?
在Python中創建功能完善的序列類型不需要使用繼承,只要實現符合序列協議的方法就可以,Python的序列協議需要__len__, __getitem__兩個方法,比如如下的Vector類:
from array import array class Vector: type_code = ‘d‘def __init__(self, compoments): self.__components = array(self.type_code, compoments) def __len__(self): return len(self.__components) def __getitem__(self, index): return self.__components[index]
我們在控制臺查看下切片功能:
>>> v1 = Vector([1, 2, 3]) >>> v1[1]2.0 >>> v1[1:2] array(‘d‘, [2.0])
在這裏我們將序列協議委托給self.__compoments(array的實例),只需要實現__len__和__getitem__,就可以支持切片功能了。
(二)那麽Python的切片工作原理又是怎樣的呢?
我們通過一個簡單的例子來查看slice的行為:
class MySequence: def __getitem__(self, index): return index
>>> s1 = MySequence() >>> s1[1] 1 >>> s1[1:4] slice(1, 4, None) >>> s1[1:4:2] slice(1, 4, 2) >>> s1[1:4:2, 7:9] (slice(1, 4, 2), slice(7, 9, None))
我們看到:
(1)輸入整數時,__getitem__返回的是整數
(2)輸入1:4表示法時,返回的slice(1, 4, None)
(3)輸入1:4:2表示法,返回slice(1, 4, 2)
(4)[]中有逗號,__getitem__收到的是元組
現在我們來仔細看看slice本身:
>>> slice <class ‘slice‘> >>> dir(slice) [‘__class__‘, ‘__delattr__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge __‘, ‘__getattribute__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__init_subclass__‘, ‘__le__‘, ‘__lt__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr_ _‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘indices‘, ‘star t‘, ‘step‘, ‘stop‘]
我們看到了熟悉的start, stop, step屬性,還有一個不熟悉的indices,用help查看下(Pyhon的控制臺是很有價值的工具,我們常常使用dir,help命令獲得幫助):
Help on method_descriptor: indices(...) S.indices(len) -> (start, stop, stride) Assuming a sequence of length len, calculate the start and stop indices, and the stride length of the extended slice described by S. Out of bounds indices are clipped in a manner consistent with the handling of normal slices.
這裏的indices能用於優雅的處理缺失索引和負數索引,以及長度超過目標序列長度的切片,這個方法會整頓輸入的slice元組,把start, stop, step都變成非負數,且落在指定長度序列的邊界內:
比如:
>>> slice(None, 10, 2).indices(5) # 目標序列長度為5,自動將stop整頓為5 (0, 5, 2) >>> slice(-1, None, None).indices(5) # 將start = -1, stop = None , step = None 整頓為(4, 5, 1) (4, 5, 1)
如果沒有底層序列作為代理,使用這個方法能節省大量時間
上面了解了slice的工作原理,我們使用它重新實現Vector類的__getitem__方法:
from array import array from numbers import Integral class Vector: type_code = ‘d‘ def __init__(self, compoments): self.__components = array(self.type_code, compoments) def __len__(self): return len(self.__components) def __getitem__(self, index): cls = type(self) if isinstance(slice, index): return cls(self.__components[index]) # 使用cls的構造方法返回Vector的實例 elif isinstance(Integral, index): return self.__components[index] else: raise TypeError("{} indices must be integers or slices".format(cls))
Fluent Python: Slice