76

内置函数有什么用slice,如何使用?
我知道的 Pythonic 切片的直接方式 - l1[start:stop:step]. 我想知道我是否有一个切片对象,那么我该如何使用它呢?

4

6 回答 6

104

您可以通过调用 slice 来创建切片,使用的字段与执行 [start:end:step] 表示法时使用的字段相同:

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
于 2010-10-12T04:43:17.733 回答
38

序列后面的方括号表示索引或切片,具体取决于括号内的内容:

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

这两种情况都由__getitem__()序列的方法处理(或者__setitem__()如果在等号的左侧)。索引或切片作为单个参数传递给方法,Python 执行此操作的方式是转换切片表示法, ( 1:10:2, 在这种情况下) 到切片对象: slice(1,10,2).

因此,如果您要定义自己的类序列类或覆盖另一个类的__getitem__or__setitem____delitem__方法,则需要测试 index 参数以确定它是否为 anint或 a 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,它接受一个参数,即对象的长度,并返回一个三元组:(start, stop, step)

于 2010-10-12T06:16:45.603 回答
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)),)
>>>
于 2010-10-12T06:20:48.513 回答
3

slice函数返回切片对象。切片对象是 Python 的内部类型之一,它针对读取性能进行了优化——它们的所有属性都是只读的。

如果希望更改默认行为,更改slice可能很有用。例如,lxml使用切片符号来访问 DOM 元素(但是,我还没有确认他们自己是如何做到的)。

于 2010-10-12T03:49:06.660 回答
1

在尝试基于 variable 回答 Subset a string 时,我记得 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 IIRC 中添加如此高级切片功能的原因之一。

更复杂的“切片”示例:

>>> 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 现在是切片的元组。

于 2019-08-18T16:32:18.367 回答
0

Slice objects let you programmatically generate and manipulate slices. Especially for multidimensional numpy arrays, and especially if you don't know the dimensionality in advance, you might want to construct slices on-the-fly to specify the axes or dimensions that you want.

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

Here our data ended up being two dimensional (4-by-5), but there was no guarantee of that. How will you request slices from zz?

One problem is that I can't manipulate Python's slice notation. It's not valid syntax outside of a slicing operation.

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

What if I could just build up the exact slice request I wanted in a loop, the way I can build up a string? Wouldn't that be great? I mean, sure you can use a string to do it, but it would be messy and requires 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+"])

So here we are: slice objects let you do this. You can assemble lists and tuples of them on-the-fly, pass them as function parameters, sort them, shuffle them, and so on.

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]])
于 2020-07-15T23:46:02.253 回答