Python切片指南,我知道Python切片,但是如何使用内置的切片对象?

82

内置函数 slice 有什么用?如何使用?
我知道 Pythonic 切片的常规方式是 l1[start:stop:step],但如果我有一个切片对象,那么如何使用它呢?


2
这对我很有效。 def slice(self, start = None, end = None, step = None): return self.obj[slice(start, end, step)] - user2290820
6个回答

109

通过使用与[start:end:step]相同的字段调用slice来创建一个切片:

sl = slice(0,4)

要使用切片,只需将其像索引一样传递给列表或字符串:

>>> s = "ABCDEFGHIJKL"
>>> sl = slice(0,4)
>>> print(s[sl])
'ABCD'

假设你有一个定长文本字段的文件。你可以定义一个切片列表,以轻松地从该文件中的每个“记录”中提取值。

data = """\
0010GEORGE JETSON    12345 SPACESHIP ST   HOUSTON       TX
0020WILE E COYOTE    312 ACME BLVD        TUCSON        AZ
0030FRED FLINTSTONE  246 GRANITE LANE     BEDROCK       CA
0040JONNY QUEST      31416 SCIENCE AVE    PALO ALTO     CA""".splitlines()


fieldslices = [slice(*fielddef) for fielddef in [
    (0,4), (4, 21), (21,42), (42,56), (56,58),
    ]]
fields = "id name address city state".split()

for rec in data:
    for field,sl in zip(fields, fieldslices):
        print("{} : {}".format(field, rec[sl]))
    print('')

# or this same code using itemgetter, to make a function that
# extracts all slices from a string into a tuple of values
import operator
rec_reader = operator.itemgetter(*fieldslices)
for rec in data:
    for field, field_value in zip(fields, rec_reader(rec)):
        print("{} : {}".format(field, field_value))
    print('')

输出:

id : 0010
name : GEORGE JETSON    
address : 12345 SPACESHIP ST   
city : HOUSTON       
state : TX

id : 0020
name : WILE E COYOTE    
address : 312 ACME BLVD        
city : TUCSON        
state : AZ

id : 0030
name : FRED FLINTSTONE  
address : 246 GRANITE LANE     
city : BEDROCK       
state : CA

id : 0040
name : JONNY QUEST      
address : 31416 SCIENCE AVE    
city : PALO ALTO     
state : CA

41

方括号用在序列后面,根据方括号里的内容表示索引或切片:

>>> "Python rocks"[1]    # index
'y'
>>> "Python rocks"[1:10:2]    # slice
'yhnrc'

这两种情况都由序列的__getitem__()方法处理(如果在等号左侧,则由__setitem__()方法处理)。索引或切片作为单个参数传递给方法,Python将切片表示法(在本例中为1:10:2)转换为切片对象:slice(1, 10, 2)

因此,如果您正在定义自己的类似序列的类或重写另一个类的__getitem____setitem____delitem__ 方法,则需要测试索引参数以确定它是int还是slice,并进行相应的处理:

def __getitem__(self, index):
    if isinstance(index, int):
        ...    # process index as an integer
    elif isinstance(index, slice):
        start, stop, step = index.indices(len(self))    # index is a slice
        ...    # process slice
    else:
        raise TypeError("index must be int or slice")

slice 对象有三个属性:startstopstep,以及一个方法:indices,它接受一个参数,即对象的长度,并返回一个 3 元组:(start, stop, step)


相关:https://dev59.com/LnA85IYBdhLWcg3wHvs0 - Ciro Santilli OurBigBook.com
有人可以解释一下 indices 方法的用法吗?我看了一下文档,但还是不太明白。 - Vlad Vladovich

8
>>> class sl:
...  def __getitem__(self, *keys): print keys
...     
>>> s = sl()
>>> s[1:3:5]
(slice(1, 3, 5),)
>>> s[1:2:3, 1, 4:5]
((slice(1, 2, 3), 1, slice(4, 5, None)),)
>>>

2
请看唐的回复,了解为什么会发生这种情况。 - Ankur Agarwal
1
同时指出一下,在 __getitem__ 中也可以传入多个切片:s[1:2:3, 1, 4:5] => (slice(1, 2, 3), 1, slice(4, 5, None)),这样会更有益处。 - OozeMeister
可能更容易理解在不使用那个解包操作符 * 之前如何将 [] 中的索引或切片传递给特殊方法 __getitem__() - Nicholas

3
slice函数返回切片对象。切片对象是Python内部类型之一,被优化为可读性能-它们的所有属性都是只读的。
如果希望更改默认行为,则更改slice可能会很有用。例如,lxml使用切片符号访问DOM元素(但我还没有自己确认他们是如何做到的)。

1

在尝试回答 如何基于变量截取字符串 时,我想起了numpy有一种语法上很好的定义切片对象的方式:

>>> import numpy as np
>>> s = "The long-string instrument is a musical instrument in which the string is of such a length that the fundamental transverse wave is below what a person can hear as a tone."
>>> z = np.s_[18:26]  # in this case same as slice(18, 26, None)
>>> s[z]
'strument'

这里解决的问题是如何将切片存储在变量中以供以后使用,np.s_允许做到这一点。是的,它不是内置的,但由于原始问题被重定向到这里,我觉得我的答案也应该在这里。此外,据我所知,numpy是为什么Python添加了如此高级的切片功能之一。

更复杂“切片”的示例:

>>> data = np.array(range(6)).reshape((2, 3))
>>> z = np.s_[:1, 1:2]
>>> data[z]
array([[1]])
>>> data
array([[0, 1, 2],
       [3, 4, 5]])
>>> z
(slice(None, 1, None), slice(1, 2, None))

其中 z 现在是切片的元组。


1
实际上,在这种情况下,np.s_[18:26]只是返回一个Python切片对象,它与使用内置的slice构造的对象相同。对于其他类型的仅限于numpy的索引方法可能会有所不同。 - GZ0
添加了,谢谢!但是我理解那个问题是如何在语法上表示切片。所以这是我所知道的最接近的方法。 - Roman Susi

0

Slice对象允许您以编程方式生成和操作切片。特别是对于多维NumPy数组,特别是如果您事先不知道维数,您可能需要动态构建切片来指定要使用的轴或维度。

import numpy as np
dimension = np.random.randint(10) # Might have up to 10 dimensions
shape = []
for d in range(dimension):
    shape.append(np.random.randint(10))
zz = np.random.rand(tuple(shape))
print(zz)
>>> array([[0.68379351, 0.50854469, 0.64578775, 0.73441699, 0.28977396],
           [0.88797164, 0.81603025, 0.63978659, 0.22677299, 0.93455738],
           [0.0892855 , 0.28048706, 0.04262895, 0.9353467 , 0.13062249],
           [0.88561035, 0.93378367, 0.12124208, 0.25600301, 0.96035638]])

这里我们的数据最终变成了二维数组(4行5列),但并不保证一定是这样。你将如何从zz中请求切片?

一个问题是我无法操纵Python的切片符号。在切片操作之外,它不是有效的语法。

my_slice = 2:3:1
>>> SyntaxError: Invalid Syntax

如果我可以像构建字符串一样,在循环中构建出我想要的精确切片请求,那该多好啊?虽然你可以使用字符串来实现,但这会很混乱,并且需要使用eval
your_slice_definitions = [(2,3,1), *[(None, None, None)]*(zz.ndim - 1)] 
my_slice_str = ""
for slice_start, slice_end, slice_step in your_slice_definitions:
    my_slice_str += "{}:{}:{},".format(slice_start, slice_end, slice_step)
eval("zz["+my_slice_str+"])

所以我们来看看:`slice`对象可以让你做到这一点。你可以即时组装列表和元组,将它们作为函数参数传递,对它们进行排序、洗牌等操作。
my_slices = []
for slice_start, slice_end, slice_step in your_slice_definitions:
    my_slices += slice(slice_start, slice_end, slice_step)
print(zz[my_slices])
>>> array([[0.0892855 , 0.28048706, 0.04262895, 0.9353467 , 0.13062249]])

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接