1:什么场景下我们会用到切片?
2:实现切片的原理,基于此,我们如何实现一个支持切片操作的自定义类型?
下面说下什么场景下我们会用到切片?
当我们想截取一段代码,怎样操作呢?
比如下面:
1 2 3 4 5 6 7 8 9 10
| list = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0] array1 = array('d', list) components = reprlib.repr(array1)
components = components[components.find('['):-1]
|
《流畅的python》示例10.2的repr就用到切片来组合一个新的“构造方法字符串”。
我们还知道切片是对象,这可以用于什么场景呢?请看下面一段代码与注释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| >>> invoice = """ ... 0....5............... ... tan 168 male ... guan 160 female ... """
>>> name = slice(0, 5) >>> height = slice(5,10) >>> gender = slice(10, None)
>>> list = invoice.split('\n')[2:] >>> for item in list: ... print(item[name], item[height]) ... tan 168 guan 160
|
切片操作的原理是什么呢?
当我们调用list[0:8]的时候,解释器其实是把slice(0,8)这个对象传给list.getitem(self,slice),下面来看 Python 如何把 my_seq[1:3] 句法变成传给 my_seq.getitem(…) 的参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| >>> class MySeq: ... def __getitem__(self, index): ... return index ... >>> s = MySeq() >>> s[1] 1 >>> s[1:4] slice(1, 4, None) >>> s[1:4:2] slice(1, 4, 2) >>> s[1:4:2, 9] (slice(1, 4, 2), 9) >>> s[1:4:2, 7:9] (slice(1, 4, 2), slice(7, 9, None))
|
getitem会根据传入的数据类型做不同的操作,下面我们通过自定义一个类,让它支持切片,来更好的理解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 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)
def __str__(self): return str(tuple(self)) def __len__(self): return len(self._components)
def __getitem__(self, index): return self._components[index]
|
执行结果如下:
1 2 3
| >>> v7 = Vector(range(7)) >>> v7[1:4] array('d', [1.0, 2.0, 3.0])”
|
这就有问题了,我们得到的切片应该也是Vector类,可现在是数组类型,如何解决呢?这就要用到我们上面说的切片原理了,我们要根据不同的类型,采取不同的手段。
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
| 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)
def __str__(self): return str(tuple(self)) def __len__(self): return len(self._components)
def __getitem__(self, index): cls = type(self) if isinstance(index, slice): return cls(self._components[index]) elif isinstance(index, numbers.Integral): return self._components[index] else: msg = '{cls.__name__} indices must be integers' raise TypeError(msg.format(cls=cls))
|
扩展思考:用上面的方法,如何实现一个字典也能切片操作呢?