
本文深入探讨了 Python 中 in 操作符在集合 (set) 和列表 (list) 这两种数据结构中的不同行为。通过分析其内部实现机制,解释了为何在某些情况下,使用 in 操作符时,列表会引发错误,而集合却能正常运行。同时,结合 PyTorch 张量 (Tensor) 的特性,提供了针对特定问题的解决方案,并给出了 in 操作符使用的注意事项。
x in collection 在 Python 中的行为取决于 collection 的类型。具体来说,使用内部哈希表的集合和字典与不使用哈希表的列表和元组的工作方式不同。
当 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),如果身份不同,则比较相等性 (==)。
立即学习“Python免费学习笔记(深入)”;
当 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 匹配的元素子集。然后,它迭代该子集,并将每个元素 c 与 x 进行比较(首先比较身份,然后比较相等性),并在找到第一个匹配项时停止。哈希表的运用使得查找速度更快,尤其是在处理大型数据集时。
注意: 集合中元素的哈希值在元素添加到集合时计算,而 x 的哈希值在使用 in 操作符时计算。
为了更清楚地理解 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={other}")
return self._val == other._val if isinstance(other, MyObj) else False
def __repr__(self):
return f"<{self.__class__.__name__}: {self._val}>"创建 MyObj 类的实例,并将其添加到集合和列表中:
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
对于列表,Python 首先检查身份 (x is c)。如果身份相同,则不需要检查相等性 (x == c)。如果身份不同,则检查相等性。
PyTorch 张量在进行相等性比较时,如果形状不一致,会引发 RuntimeError。这是导致原始问题中列表引发错误的原因。
import torch a = torch.Tensor(2,3) b = torch.Tensor(2) # case 1a: # b in list([a,a,b]) # raises an error: # 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]) 中,由于 a 和 b 的形状不同,在进行 b == a 比较时会引发 RuntimeError。而在 b in set([a, b]) 中,由于集合首先进行哈希值比较,而 torch.Tensor 的哈希值是其内存地址,因此在绝大多数情况下 hash(a) 和 hash(b) 是不同的,从而避免了 b == a 的比较,避免了错误。
一种解决方案是使用 torch.Tensor.size() 属性(它是元组的子类),并创建一个字典或集合,用于存储不同大小的张量。
import torch
tensors_by_size = {}
a = torch.Tensor(2, 3)
b = torch.Tensor(2)
size_a = tuple(a.size())
size_b = tuple(b.size())
if size_a not in tensors_by_size:
tensors_by_size[size_a] = set()
tensors_by_size[size_a].add(a)
if size_b not in tensors_by_size:
tensors_by_size[size_b] = set()
tensors_by_size[size_b].add(b)
# Now you can check if a tensor of a certain size exists
target_tensor = torch.Tensor(2)
target_size = tuple(target_tensor.size())
if target_size in tensors_by_size:
print("Tensor of size", target_size, "exists.")
if target_tensor in tensors_by_size[target_size]:
print("Tensor exists in the collection")
else:
print("Tensor of that size exists, but not that specific tensor.")
else:
print("Tensor of size", target_size, "does not exist.")in 操作符在 Python 中对于集合和列表的行为有所不同。理解其内部实现机制,可以帮助我们更好地利用这些数据结构,并避免潜在的错误。在处理 PyTorch 张量等特殊类型时,需要特别注意其相等性比较的规则,并采取相应的解决方案。使用哈希表可以优化查找速度,但在某些情况下,可能会掩盖潜在的错误。因此,在选择数据结构时,需要根据实际情况进行权衡。
以上就是Python中 in 操作符在集合与列表中的不同行为详解的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号