我应该如何处理Python中的包含范围?

2024-04-28 04:14:54 发布

您现在位置:Python中文网/ 问答频道 /正文

我在一个领域工作,在这个领域中,范围通常被包括在内地描述。我有人类可读的描述,例如from A to B,它表示包含两个端点的范围,例如from 2 to 4表示2, 3, 4

在Python代码中使用这些范围的最佳方法是什么?以下代码可用于生成包含范围的整数,但我还需要执行包含切片操作:

def inclusive_range(start, stop, step):
    return range(start, (stop + 1) if step >= 0 else (stop - 1), step)

我看到的唯一完整解决方案是每次使用+ 1(或- 1)或切片表示法(例如range(A, B + 1)l[A:B+1]range(B, A - 1, -1))时显式使用+ 1(或- 1)。这种重复真的是处理包含范围的最佳方法吗?

编辑:感谢L3viathan的回答。写一个inclusive_slice函数来补充inclusive_range当然是一个选项,尽管我可能会这样写:

def inclusive_slice(start, stop, step):
    ...
    return slice(start, (stop + 1) if step >= 0 else (stop - 1), step)

这里的...表示处理负索引的代码,这在与切片一起使用时并不简单-注意,例如,如果slice_to == -1,l3viaan的函数会给出不正确的结果。

然而,似乎一个inclusive_slice函数会很难使用-l[inclusive_slice(A, B)]真的比l[A:B+1]更好吗?

有没有更好的方法来处理包含的范围?

编辑2:感谢您提供新答案。我同意Francis和Corley的观点,即改变slice操作的含义,无论是全局的还是某些类的,都会导致严重的混淆。因此,我现在倾向于编写一个inclusive_slice函数。

为了回答我之前编辑的问题,我得出的结论是,使用这样的函数(例如l[inclusive_slice(A, B)])比手动加/减1(例如l[A:B+1])要好,因为它允许在一个地方处理边案例(例如B == -1B == None)。我们能减少使用这个函数时的尴尬吗?

编辑3:我一直在考虑如何改进用法语法,它目前看起来像l[inclusive_slice(1, 5, 2)]。特别是,如果创建的包含切片类似于标准切片语法,那将是很好的。为了允许这样做,而不是inclusive_slice(start, stop, step),可能有一个函数inclusive将切片作为参数。inclusive的理想用法语法是行1

l[inclusive(1:5:2)]          # 1
l[inclusive(slice(1, 5, 2))] # 2
l[inclusive(s_[1:5:2])]      # 3
l[inclusive[1:5:2]]          # 4
l[1:inclusive(5):2]          # 5

不幸的是,Python不允许这样做,它只允许在[]中使用:语法。inclusive因此必须使用语法23调用(其中s_的作用类似于the version provided by numpy)。

其他的可能性是使inclusive成为一个具有__getitem__的对象,允许语法4,或者只将inclusive应用于切片的stop参数,如语法5。不幸的是,我不相信后者能够工作,因为inclusive需要了解step值。

在可行的语法中(原始的l[inclusive_slice(1, 5, 2)],加上234),哪一个是最好的?或者还有其他更好的选择吗?

最后编辑:感谢大家的回复和评论,这非常有趣。我一直都是Python“单向”哲学的粉丝,但这个问题是由Python的“单向”和问题领域所禁止的“单向”之间的冲突引起的。我确实对语言设计中的TIMTOWTDI有些欣赏。

因为我给出了第一个也是最高票数的答案,所以我把奖金颁给了L3viathan。


Tags: to方法函数代码编辑step语法切片
3条回答

为inclusive slice编写一个附加函数,并使用该函数而不是切片。例如,子类list和实现一个对slice对象作出反应的__getitem__是可能的,但我建议不要这样做,因为您的代码将在一年内表现出与除您之外的任何人的预期相反的行为,可能也与您的预期相反。

inclusive_slice可能是这样的:

def inclusive_slice(myList, slice_from=None, slice_to=None, step=1):
    if slice_to is not None:
        slice_to += 1 if step > 0 else -1
    if slice_to == 0:
        slice_to = None
    return myList[slice_from:slice_to:step]

我个人会做的,就是使用你提到的“完整”解决方案(range(A, B + 1)l[A:B+1])并好好评论。

如果您不想指定步骤大小,而是指定步骤数,则可以使用numpy.linspace选项,该选项包括起点和终点

import numpy as np

np.linspace(0,5,4)
# array([ 0.        ,  1.66666667,  3.33333333,  5.        ])

因为在Python中,结束索引总是独占的,所以在内部始终使用“Python约定”值是值得考虑的。这样,您就可以避免在代码中混淆这两者。

仅通过专用转换子程序处理“外部表示”:

def text2range(text):
    m = re.match(r"from (\d+) to (\d+)",text)
    start,end = int(m.groups(1)),int(m.groups(2))+1

def range2text(start,end):
    print "from %d to %d"%(start,end-1)

或者,可以用true Hungarian notation标记包含“异常”表示的变量。

相关问题 更多 >