Python中如何使用字符串创建切片对象。

12

我想从一个字符串创建切片对象;目前唯一的方法似乎是通过冗长和繁琐的eval语句。

class getslice:
    def __getitem__(self, idx): return idx[0]
eval("getslice()[%s, 1]" %(":-1"))

提前谢谢。

编辑: 如果原问题没有表述清楚,在这种情况下输入是“:-1”。重点是解析字符串。Ignacio Vazquez-Abrams的回答至少解决了问题(并且似乎也适用于反向索引),但我认为我的解决方案仍然更清晰,尽管不太概念上干净(如果Python改变切片语法,它也将正确工作)。


你能否更清楚地说明你想要的行为是什么? - Andrew Jaffe
为什么要创建独立的切片对象?切片是一流的语法结构,如果不知道为什么要这样做,这个问题就很奇怪。 - S.Lott
Jaffe:问题已更新。 Lott:我正在进行一些语言处理工作,希望也能使用Python切片。实际对象非常有用:它具有indices(len)函数,可以在给定数组长度的情况下提供(开始,停止,增量)。 - gatoatigrado
@gatoatigrado:是的,Slice()对象很有用。内置的slice函数有什么问题吗?问题在于它无法正确解析源表示形式吗? - S.Lott
也许我很蠢,但我仍然不明白你想要什么! - Andrew Jaffe
1
内置的切片函数需要三个参数 - 开始、停止和步长。我想解析一个字符串,例如 "0:1" --> slice(0, 1, None); ":-1" --> slice(None, -1, None), "3::2" --> slice(3, None, 2)。 - gatoatigrado
11个回答

6

slice(*map(lambda x: int(x.strip()) if x.strip() else None, mystring.split(':')))

对于只有单个参数的切片,如 '-1''1',当 mystring.split(':')==1 时,只需调用 int(x)

按要求取出注释部分。


一种更加Pythonic的方法是不使用map,而是像这样创建一个生成器:slice(*(int(part) if part else None for part in sslice.split(':'))). 如果你想支持字段中的空格,那么可以使用以下两种方式之一:slice(*(int(part.strip()) if part.strip() else None for part in sslice.split(':'))) 或者 slice(*(int(spart) if spart else None for part in sslice.split(':') for spart in [part.strip()])). - Marandil

3
如果你想要一个切片对象,为什么不直接实例化一个呢?
s = slice(start, stop, step)

你所说的“从字符串创建”是什么意思?

3
slice(*[{True: lambda n: None, False: int}[x == ''](x) for x in (mystring.split(':') + ['', '', ''])[:3]])

11
这有点不正确,例如:“1:1:1:1”不是一个正确的切片字符串,“1 : 1 : 1”才是。应该像这样修改:slice(*map(lambda x: int(x.strip()) if x.strip() else None, mystring.split(':')))。 - pprzemek
2
pprzemek,我认为你的评论包含了最好的答案,为什么不将其制作成真正的答案呢? - proggy
@pprzemek 你好,DanielJung说得对,你真的应该把你的评论转换成一个答案。 - SebMa
可能看起来有些丑陋,但似乎被选中的答案是正确的。如果你在字符串“-1”上尝试所选的答案,你会得到slice(-1, None, None),但如果你尝试pprzemeks的答案,你会得到slice(None, -1, None),这是不正确的。 - StefanH
该代码无法正确处理带有“since number”的情况,因为slice(2) == slice(None, 2, None),而不是slice(2, None, None)。 - Bohumir Zamecnik
@StefanH 我觉得这很明显,但如果字符串中没有“:”,当然你只需调用int(str_slice),但其中并没有什么魔法,这里的问题是更复杂的切片。 - pprzemek

2

我来到这里是因为我想让我的脚本接受类似于Python的切片参数,并将其渲染成整数列表,我使用了一个函数,看起来回答了OP的问题:

# create a slice object from a string
def get_slice_obj(slicearg):
    slice_ints = tuple([ int(n) for n in slicearg.split(':') ])
    return apply(slice, slice_ints)

def ints_from_slicearg(slicearg):
    slice_obj = get_slice_obj(slicearg)
    return(range(slice_obj.start or 0, slice_obj.stop or -1, slice_obj.step or 1))

for t in ['1', '1:3', '4:9:2']:
    print t, "=>", ints_from_slicearg(t)

输出:

1 => [0]
1:3 => [1, 2]
4:9:2 => [4, 6, 8]

1
这里有另一种方法(仅是此处发布的其他方法的汇总):
def make_slice(expr):
    def to_piece(s):
        return s and int(s) or None
    pieces = map(to_piece, expr.split(':'))
    if len(pieces) == 1:
        return slice(pieces[0], pieces[0] + 1)
    else:
        return slice(*pieces)

使用示例:

In [1]: make_slice(':')
Out[1]: slice(None, None, None)

In [2]: make_slice(':-2')
Out[2]: slice(None, -2, None)

In [3]: x = [1, 2, 3]

In [4]: x[make_slice('::-1')]
Out[4]: [3, 2, 1]

1
Ignacio Vazquez-Abrams的一行代码短小精悍,但阅读起来很困难,并且在处理单个数字时与“slice”不一致。这种方法尝试以更清晰的方式解析它。
def parse_slice(value):
    """
    Parses a `slice()` from string, like `start:stop:step`.
    """
    if value:
        parts = value.split(':')
        if len(parts) == 1:
            # slice(stop)
            parts = [None, parts[0]]
        # else: slice(start, stop[, step])
    else:
        # slice()
        parts = []
    return slice(*[int(p) if p else None for p in parts])

# unit tests:
try:
    assert parse_slice('')
    assert False, 'It should raise TypeError'
except TypeError:
    pass
assert parse_slice('2') == slice(2)
assert parse_slice('2:3') == slice(2, 3)
assert parse_slice(':3') == slice(None, 3)
assert parse_slice(':') == slice(None, None)
assert parse_slice('2:') == slice(2, None)
assert parse_slice('2:3:4') == slice(2, 3, 4)
assert parse_slice(':3:4') == slice(None, 3, 4)
assert parse_slice('2::4') == slice(2, None, 4)
assert parse_slice('2:3:') == slice(2, 3, None)
assert parse_slice('::4') == slice(None, None, 4)
assert parse_slice('2::') == slice(2, None, None)
assert parse_slice('::') == slice(None, None, None)
assert parse_slice('-12:-13:-14') == slice(-12, -13, -14)
assert parse_slice('2:3:-4') == slice(2, 3, -4)
try:
    parse_slice('1:2:3:4')
    assert False, 'It should raise TypeError'
except TypeError:
    pass

0

基于 @pprzemak 的草案,以下是用于精细解析的函数:

def parse_slice(v: Text):
    """
    Parses text like python "slice" expression (ie ``-10::2``).

    :param v:
        the slice expression or a lone integer
    :return:
        - None if input is None/empty
        - a ``slice()`` instance (even if input a lone numbrt)
    :raise ValueError:
        input non-empty but invalid syntax
    """
    orig_v = v
    v = v and v.strip()
    if not v:
        return

    try:
        if ':' not in v:
            ## A lone number given.
            v = int(v)
            return slice(v, v + 1)

        return slice(*map(lambda x: int(x.strip()) if x.strip() else None,
                          v.split(':')))
    except Exception:
        pass

    ## An alternative is to return `slice(None)` here.
    raise trt.TraitError("Syntax-error in '%s' slice!" % orig_v)

0
这个怎么样(对于简单的非空切片间隔):
sliceStr = "3:8"
mySlice = slice( *map(int, sliceStr.split(':') ) )

0

我只是需要在12年后做这件事,所以这里是我的答案,使用正则表达式 :)

import re
def parse_slice(string: str) -> slice:
    """
    Parse a string representation of a slice and return a slice object
    """
    # Matches one required colon, one optional colon, and up to three
    # positive or negative numbers between them
    match = re.match(r"^(-?[\d]*):(-?[\d]*)[:]?(-?[\d]*)$", string)
    if match:
        args = tuple(map(lambda s: int(s) if s else None, match.group(1, 2, 3)))
        return slice(*args)
    raise ValueError("Could not parse slice")

-1

切片对象通常使用下标符号创建,该符号在slice()文档中内部使用slice()。你想要做的是:

your_string[start:end]

来自Python教程

字符串可以被下标(索引)访问;就像在C语言中,字符串的第一个字符的下标(索引)为0。没有单独的字符类型;一个字符只是大小为1的字符串。与Icon类似,子字符串可以使用切片表示法指定:两个由冒号分隔的索引。

>>> word = 'Help' + 'A' 
>>> word[4]
'A'
>>> word[0:2]
'He'
>>> word[2:4]
'lp'

切片索引具有有用的默认值;省略第一个索引默认为零,省略第二个索引默认为被切片字符串的大小。
>>> word[:2]    # The first two characters
'He'
>>> word[2:]    # Everything except the first two characters
'lpA'

抱歉,那不是我想要的。我想从字符串中解析出切片,例如“-3::2”,并返回一个切片对象[请参见原始问题]。 - gatoatigrado
从你的问题中并不清楚那是否是你所询问的。很抱歉我无法再为你提供更多帮助,但我建议你阅读《Python之禅》,并重新考虑你所尝试的行为是否违背它。如果是的话,那么这可能是一个不好的想法。 - mpeterson
当然,我认为我的大部分代码都符合要求(感谢您提供的参考)。很抱歉我不知道如何更清楚地解释;您有阅读过修改后的问题(以及下面的评论)吗?祝好, 尼古拉斯 - gatoatigrado
从你的问题中并不清楚你在问什么。你能解释得更详细一些吗?哪里不清楚,我该如何改进呢? - gatoatigrado

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