为什么Python3的范围/切片对象不支持其他范围/切片的包含测试?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么Python3的范围/切片对象不支持其他范围/切片的包含测试?相关的知识,希望对你有一定的参考价值。

Python3的范围对象支持对整数进行O(1)包含检查(1) (2)

因此,人们可以做15 in range(3, 19, 2)并得到正确答案True

但是,它不支持包含检查b / w两个范围

a = range(0, 10)
b = range(3, 7)
a in b # False
b in a # False, even though every element in b is also in a
a  < b # TypeError: '<' not supported between instances of 'range' and 'range'

似乎b in a被解释为'a'范围内的任何元素等于对象'b'?

但是,由于范围不能包含除整数之外的任何内容,因此range(...) in range(...)将始终返回False。恕我直言,这样的查询应该像范围'a'中范围'b'中的每个元素一样被回答?鉴于该范围仅存储开始,停止,步长和长度,该查询也可以在O(1)中回答。

切片对象也没有帮助。它没有实现__contains__方法,而__lt__方法只是compares two slices as tuples(这没有意义)

目前这些实施背后有原因,还是只是“它碰巧以这种方式实施”的事情?

答案

看起来__contains__对范围的实现是range_contains,它只是检查给定元素是否在迭代中,具有longs的特殊情况。

如你所知,e in b返回true iff eb中的一个元素。任何其他实现,例如检查e是否是b子集的实现,都是不明确的。这在Python中尤其成问题,它不需要迭代是同构的,并且允许嵌套列表/元组/集/可迭代。

考虑一个实现所需行为的世界。现在,假设我们有以下列表:

my_list = [1, 2, (3, 4), 5]
ele1 = (3, 4)
ele2 = (2, 5)

在这种情况下,ele1 in my_listele2 in my_list应该返回什么?你想要的行为使得编写诸如此类的代码变得冗长乏味

if e in my_iterable:
    # ah! e must exist!
    my_iterable.remove(e)

更安全,更好的方法是保持当前行为,而是使用不同的类型敏感运算符来实现子集谓词:

x = set([1])
y = set([1,2])
x < y  # True
[1] < y  # raises a TypeError
另一答案

你把'b' containing 'a''a' being a subset of 'b'搞混了 - 这是两件不同的事情。

b containing a意味着range(0, 10)b内。让我们说:

a = [1, 2, 3]

b = [1, 2, 3, 4, 5]

a in b只有真正的名单[1, 2, 3][[1, 2, 3], 4, 5]才是真的。所以你实际上是在检查列表本身是否在另一个列表中,而不是所有元素都在另一个列表中。

如果a的所有元素都在b内,则列表ab的子集。在你的例子中,ba的一个子集,是的,但实际列表b不是IN a

如果要执行此类方法,则可能建议您使用set数据结构

另一答案

Range对象实现collections.abc.Sequence,它支持包含测试。

a in b 
b in a

在这种情况下,您正在搜索范围b中的Range对象a,反之亦然。应该是假的。

以上是关于为什么Python3的范围/切片对象不支持其他范围/切片的包含测试?的主要内容,如果未能解决你的问题,请参考以下文章

Go 在数组子切片上的内置范围具有不一致的行为

为啥切片和范围上限是排他的?

为啥索引超出范围的子字符串切片有效?

字符串切片的范围不一致

Python pandas 按多个索引范围切片数据帧

当切片索引超出范围时如何引发 IndexError?