
本文深入探讨了 Python 中 in 运算符在不同数据结构(尤其是列表和集合)中的行为差异。通过分析内部实现机制,解释了为何在特定场景下,使用列表会引发错误,而使用集合却能正常运行。同时,结合 PyTorch 张量的特性,提供了针对性解决方案和代码示例,帮助读者更好地理解和应用 in 运算符。
在 Python 中,in 运算符用于检查某个元素是否存在于一个集合(collection)中。然而,对于不同类型的集合,其内部实现机制存在差异,导致在某些情况下会产生不同的结果。本文将深入探讨 in 运算符在列表(list)和集合(set)中的行为差异,并结合具体的 PyTorch 张量示例,解释其背后的原因,并提供相应的解决方案。
x in collection 的具体行为取决于 collection 的类型。通常,使用内部哈希表的数据结构(如集合和字典)与不使用哈希表的数据结构(如列表和元组)的处理方式不同。
对于列表和元组等不使用哈希表的集合,x in collection 的内部实现逻辑如下(伪代码):
立即学习“Python免费学习笔记(深入)”;
def is_in(x, collection):
for c in collection:
if (x is c or x==c):
return True
return False
该过程会遍历集合中的每个元素 c,依次进行身份比较 (x is c) 和相等性比较 (x == c),直到找到第一个匹配项。
对于集合和字典等使用哈希表的集合,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该过程首先根据 x 的哈希值,从集合中筛选出哈希值相同的元素子集 subset。然后,遍历 subset 中的每个元素 c,依次进行身份比较和相等性比较,直到找到第一个匹配项。哈希表的应用显著提高了查找效率,尤其是在大型数据集中。
为了更深入地理解 in 运算符的行为,我们创建一个自定义类 MyObj,并定义其哈希计算逻辑 (__hash__) 和相等性判断逻辑 (__eq__):
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"<{self.__class__.__name__}: {self._val}>"接下来,创建 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__ 方法进行相等性判断。
>>> s
{<MyObj: a>, <MyObj: b>, <MyObj: d>}
>>> b in s
<MyObj: b> calling __hash__
True
>>> d in s
<MyObj: d> calling __hash__
<MyObj: b> calling __eq__, other=<MyObj: d>
<MyObj: d> calling __eq__, other=<MyObj: b>
True在集合中使用 in 运算符时,Python 首先计算 x 的哈希值。如果存在哈希冲突,则会调用 __eq__ 方法进行相等性判断。
>>> lst [<MyObj: a>, <MyObj: b>, <MyObj: d>] >>> a in lst True >>> b in lst <MyObj: a> calling __eq__, other=<MyObj: b> <MyObj: b> calling __eq__, other=<MyObj: a> True >>> d in lst <MyObj: a> calling __eq__, other=<MyObj: d> <MyObj: d> calling __eq__, other=<MyObj: a> <MyObj: b> calling __eq__, other=<MyObj: d> <MyObj: d> calling __eq__, other=<MyObj: b> True
在列表中使用 in 运算符时,Python 会依次遍历列表中的每个元素,首先进行身份比较 (x is c)。如果身份比较失败,则调用 __eq__ 方法进行相等性判断。
在 PyTorch 中,如果尝试比较两个大小不同的张量,会引发 RuntimeError。PyTorch 张量的哈希值是通过 id(self) 计算的,即对象的内存地址。
import torch a = torch.Tensor(2,3) b = torch.Tensor(2) # case 1a: # b in list([a,a,b]) # raises an error: # Traceback (most recent call last): # File "<stdin>", line 1, in <module> # RuntimeError: The size of tensor a (2) must match the size of tensor b (3) at non-singleton dimension 0 # case 1b b in set([a,a,b]) # True (i.e. no error)
当执行 b in list([a, b]) 时,会依次进行以下比较:
由于 b == a 引发了错误,因此永远不会将 b 与列表中的 b 进行比较。
当执行 b in set([a, a, b]) 时,由于集合使用哈希表,会首先比较哈希值。因为 Tensor.__hash__ 返回张量的 id,所以集合的哈希表类似于 {id(a): a, id(b): b}。b in s 的执行过程如下:
因此,不会引发 RuntimeError。
总结: 列表会按顺序检查 (x is c or x==c),而集合会首先检查哈希值,然后迭代集合中具有相同哈希值的所有项目,以检查 (x is c or x==c)。由于 hash(b) != hash(a),因此几乎永远不会比较 b == a,从而避免了 RuntimeError。
为了解决这个问题,可以利用 torch.Tensor.size 属性(它是元组的子类),并创建一个字典,其中键是张量的大小,值是具有该大小的张量的集合(或列表)。
import torch
tensors = {}
a = torch.Tensor(2, 3)
b = torch.Tensor(2)
c = torch.Tensor(2, 3)
def add_tensor(tensor, tensors):
size = tuple(tensor.size())
if size not in tensors:
tensors[size] = set()
tensors[size].add(tensor)
add_tensor(a, tensors)
add_tensor(b, tensors)
add_tensor(c, tensors)
def check_tensor(tensor, tensors):
size = tuple(tensor.size())
return tensor in tensors.get(size, set())
print(check_tensor(b, tensors)) # Output: True
print(check_tensor(torch.Tensor(2), tensors)) # Output: False本文详细分析了 Python 中 in 运算符在列表和集合中的不同行为,并结合 PyTorch 张量的特殊情况,解释了引发 RuntimeError 的原因。通过理解其内部实现机制,可以更好地选择合适的数据结构,并避免潜在的错误。同时,本文提供了针对性的解决方案,帮助读者在实际应用中更好地处理类似问题。
以上就是Python 中 in 运算符在集合和列表中的不同行为解析的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号