Python 中 in 运算符在集合和列表中的不同行为

python 中 in 运算符在集合和列表中的不同行为

Python 中 in 运算符在集合和列表中的不同行为

本文深入探讨了 Python 中 in 运算符在集合(set)和列表(list)中的不同行为。通过分析其内部实现机制,解释了为何在特定情况下,使用 in 运算符在列表中会引发错误,而在集合中却能正常运行。此外,还提供了自定义类和 Pytorch 张量的示例,以帮助读者更好地理解和应用 in 运算符,并针对 Pytorch 张量比较问题,提供了一种基于张量大小的解决方案。

在 Python 中,in 运算符用于检查某个元素是否存在于一个集合中。然而,其行为在不同类型的集合(如列表和集合)中有所不同。理解这些差异对于编写高效且健壮的代码至关重要。

in 运算符的工作原理

x in collection 的工作方式取决于所使用的集合类型。具体来说,使用内部哈希表的集合(如集合和字典)与不使用哈希表的集合(如列表和元组)的工作方式不同。

1. 不使用哈希表的集合(列表、元组等)

立即学习“Python免费学习笔记(深入)”;

当 collection 是列表或元组时,x in collection 的内部实现类似于以下伪代码:

def is_in(x, collection):  for c in collection:      if (x is c or x==c):          return True  return False

依次比较 collection 中的每个元素 c 与 x,首先进行身份比较 (is),如果身份不同,则进行相等性比较 (==)。只要找到第一个匹配项,就返回 True。

2. 使用哈希表的集合(集合、字典等)

当 collection 是集合或字典时,x in collection 的内部实现如下:

def is_in(x, collection):  # 选择集合中哈希值与 x 相同的元素子集  subset = get_subset_by_hash(collection, hash(x))  for c in subset:      if (x is c or x==c):          return True  return False

首先,从 collection 中选择哈希值与 x 相同的元素子集 subset。这是集合查找速度快的原因。然后,迭代 subset 中的每个元素 c,进行身份比较和相等性比较,并在找到第一个匹配项时停止。即使对于非常大的数据集,subset 通常也只有 1 个或几个元素。注意:集合中元素的哈希值在添加到集合时计算,而 x 的哈希值在使用 in 运算符时计算。

示例:自定义类

为了更好地理解 in 运算符的行为,我们可以创建一个自定义类 MyObj,并定义其自己的哈希计算逻辑 (hash(x)) 和相等性逻辑 (x == c):

class MyObj:    def __init__(self, val, hashval):        self._val = val        self._hashval = hashval    def __hash__(self):        print(f"{str(self)} calling __hash__")        return self._hashval    def __eq__(self, other):        print(f"{str(self)} calling __eq__, {other=}")        return super().__eq__(other)    def __repr__(self):        return f""

然后,创建几个 MyObj 实例,并创建一个集合 s 和一个列表 lst:

a = MyObj("a", 123)b = MyObj("b", 456)d = MyObj("d", 456)  # 与 b 相同的哈希值!print("Creating set `s`")s = set([a, b, d])print("Creating list `lst`")lst = [a, b, d]

创建集合时,Python 会计算元素的哈希值。如果存在哈希冲突(例如,b 和 d 具有相同的哈希值),则还需要调用 __eq__。

1. 集合中的 in 运算符

>>> s{, , }>>> b in s calling __hash__True>>> d in s calling __hash__ calling __eq__, other= calling __eq__, other=True

Python 首先计算 x 的哈希值。如果存在哈希冲突,Python 还需要调用 __eq__,因此也会调用 x == c。

2. 列表中的 in 运算符

>>> lst[, , ]>>> a in lstTrue>>> b in lst calling __eq__, other= calling __eq__, other=True>>> d in lst calling __eq__, other= calling __eq__, other= calling __eq__, other= calling __eq__, other=True

Python 首先检查 x is c,如果结果为 True(身份检查),则不需要检查 x == c 的相等性。如果身份检查结果为 False,则 Python 会检查 x == c 的相等性。

Pytorch 张量中的注意事项

在 Pytorch 中,如果尝试比较大小不同的张量 a 和 b,则会引发 RuntimeError。torch.Tensor 的哈希值计算只是返回 id(self)。

当执行 b in list([a, b]) 时,会引发错误,因为逻辑会比较:

id(b) is id(a) -> Falseb == a -> 引发 RuntimeError,因此永远不会将 b 与列表中的 b 进行比较。

当执行 b in set([a, a, b]) 时,不会引发错误,因为集合会转换为类似于 s = {id(a): a, id(b): b} 的哈希表。b in s 执行以下操作:

“hash(b) 是否与 s 的哈希表中的任何哈希值相同?是的!”b is b?(in 左侧的 b 是否与集合中哈希值与 b 相同的对象相同?)。这是 True。

总结:b in set([a, b]) 和 b in [a, b] 之间的区别在于,对于列表,将按顺序检查 (x is c or x==c),而对于集合,将首先检查哈希值,然后迭代集合中具有相同哈希值的所有项以检查 (x is c or x==c)。由于 hash(b) != hash(a),几乎永远不会比较 b == a,从而在大多数情况下避免了 RuntimeError。

解决方案

为了解决 Pytorch 张量比较的问题,可以利用 torch.Tensor.size 属性(它是元组的子类),并创建一个集合(或列表)字典,用于存储不同大小的张量。

例如:

import torchtensors_by_size = {}a = torch.Tensor(2, 3)b = torch.Tensor(2)c = torch.Tensor(2, 3)def add_tensor(tensor):    size = tuple(tensor.size())    if size not in tensors_by_size:        tensors_by_size[size] = set()    tensors_by_size[size].add(tensor)add_tensor(a)add_tensor(b)add_tensor(c)def check_tensor(tensor):    size = tuple(tensor.size())    return size in tensors_by_size and tensor in tensors_by_size[size]print(check_tensor(a)) # Trueprint(check_tensor(torch.Tensor(2,3))) # False 因为是新的tensor对象print(check_tensor(b)) # True

通过这种方式,可以避免直接比较大小不同的张量,从而避免 RuntimeError。

以上就是Python 中 in 运算符在集合和列表中的不同行为的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1373703.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月14日 13:29:50
下一篇 2025年12月14日 13:29:59

相关推荐

发表回复

登录后才能评论
关注微信