赞
踩
Pyhton中序列类型支持切片功能,比如list:
>>> numbers = [1, 2, 3, 4, 5]>>> numbers[1:3]
[2, 3]
tuple也是序列类型,同样支持切片。
(一)我们是否可以使自定义类型支持切片呢?
在Python中创建功能完善的序列类型不需要使用继承,只要实现符合序列协议的方法就可以,Python的序列协议需要__len__, __getitem__两个方法,比如如下的Vector类:
from array importarrayclassVector:
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的行为:
classMySequence: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
>>>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 startandstop
indices,andthe stride length of the extended slice described by
S. Out of bounds indices are clippedina 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 importarrayfrom numbers importIntegralclassVector:
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)ifisinstance(slice, index):return cls(self.__components[index]) #使用cls的构造方法返回Vector的实例
elifisinstance(Integral, index):return self.__components[index]else:raise TypeError("{} indices must be integers or slices".format(cls))
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。