fluent python 2nd edition_Fluent Python: Slice

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))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值