0%

Python_切片原理

切片是Python重要的特性之一.与切片密切相关的是索引.一般情况下索引的返回值是序列的元素,切片的返回值是序列:

如下:

1
2
3
4
5
t = tuple(i for i in range(10))
print(t[1])
# 1
print(t[1:3])
# (1, 2)

而在序列调用[ ] 时,默认调用的是__getitem__方法.

为了能够构建符合Python风格的索引与切片操作,或者为了学习Python的切片原理,我们对其切片进行分析.

定义一个索引返回类进行查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class mySeq:
def __getitem__(self, index):
return index
s = mySeq()
print(s[1])
# 1
print(s[1:4])
# slice(1, 4, None)
print(s[1:4:2])
# slice(1, 4, 2)
print(s[1:4:2,9])
# (slice(1, 4, 2), 9)
print(s[1:4:2 , -5 :])
# (slice(1, 4, 2), slice(-5, None, None))
  • 当仅仅进行索引操作时返回的是数字
  • 当仅一个切片操作时返回的是slice对象
  • 当不仅一个切片操作时返回的时slice元组

slice类情况分析

1
2
3
4
5
print(slice)
print(dir(slice))
#<class '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', 'start', 'step', 'stop']

上面的slice对象包含有诸多属性,其中start,stop,step我们都很清楚了.indices属性很关键.我们可以看下其解释:

1
2
3
4
5
6
7
8
9
10
print(help(slice.indices))
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.

None

那么slice的作用就是把你输入的start,stop,step进行计算,获得正确的索引:例如:

1
2
3
4
print(slice(None , 10 ,2).indices(10))
print(slice(-3,None , None).indices(5))
(0, 10, 2)
(2, 5, 1)

这里我们已经知道getitem中该如何操作啦:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from array import array
import reprlib
import math
import numbers
import functools
import operator
import itertools # <1>
class Vector:
typecode = 'd'

def __init__(self, components):
self._components = array(self.typecode, components)

def __iter__(self):
return iter(self._components)

def __repr__(self):
components = reprlib.repr(self._components)
components = components[components.find('['):-1]
return 'Vector({})'.format(components)

# BEGIN VECTOR_V2
def __len__(self):
return len(self._components)

def __getitem__(self, index):
cls = type(self) # <1>
if isinstance(index, slice): # <2>
return cls(self._components[index]) # <3>
elif isinstance(index, numbers.Integral): # <4>
return self._components[index] # <5>
else:
msg = '{cls.__name__} indices must be integers'
raise TypeError(msg.format(cls=cls)) # <6>
vec = Vector(i for i in range(10))
print(vec[1:3])
# Vector([1.0, 2.0])

总结:

Python中在切片或者索引的时候都会走__getitem__方法,对于切片来说会自动将输入范围定义为slice对象,通过解析此slice对象,可以获得正确的索引.