
本文深入探讨了 Python 中 in 操作符在列表和集合这两种数据结构中的不同行为。通过分析其内部实现机制,揭示了为何在某些情况下,使用列表会引发错误,而使用集合却能正常运行。此外,本文还提供了一个自定义类示例,用于更直观地理解 in 操作符的工作原理,并针对 PyTorch 张量比较问题,提出了相应的解决方案。
在 Python 中,in 操作符用于检查某个元素是否存在于一个集合(collection)中。然而,其行为会根据集合的类型(如列表、元组、集合、字典等)而有所不同。理解这些差异对于编写高效且无错的代码至关重要。
x in collection 的行为取决于 collection 的类型。
对于列表和元组等不使用哈希表的集合,in 操作符会遍历集合中的每个元素,并逐一进行比较。其内部逻辑类似于以下伪代码:
def is_in(x, collection):
for c in collection:
if (x is c or x == c):
return True
return False首先比较 x 和 c 的身份(使用 is),如果身份相同,则返回 True。否则,比较它们的值是否相等(使用 ==)。这个过程会持续到找到第一个匹配项或遍历完整个集合。
立即学习“Python免费学习笔记(深入)”;
对于集合和字典等使用哈希表的集合,in 操作符的查找过程更加高效。其内部逻辑类似于以下伪代码:
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 的哈希值,从集合中选择一个子集,该子集包含所有哈希值与 x 相同的元素。然后,遍历这个子集,并进行身份和相等性比较。由于哈希表能够快速定位到可能的匹配项,因此查找速度通常比列表快得多。
注意: 集合中元素的哈希值是在元素添加到集合时计算的,而 x 的哈希值是在使用 in 操作符时计算的。
为了更直观地理解 in 操作符的工作原理,我们可以创建一个自定义类 MyObj,并自定义其哈希计算逻辑和相等性比较逻辑:
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 实例,并分别添加到集合和列表中:
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]通过观察输出,我们可以看到:
接下来,我们可以使用 in 操作符来检查元素是否存在于集合和列表中:
>>> 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>>> 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
通过观察输出,我们可以看到:
在 PyTorch 中,如果尝试比较大小不同的张量,会引发 RuntimeError。这是因为 PyTorch 会检查张量的形状是否兼容。
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)
当使用 in 操作符在列表中查找张量时,会按照顺序比较每个张量。如果遇到大小不同的张量,就会引发 RuntimeError。
而当使用 in 操作符在集合中查找张量时,由于集合使用哈希表,因此会首先比较哈希值。在 PyTorch 中,张量的哈希值实际上是其内存地址(id(self))。因此,只有当 b 与集合中某个张量的内存地址相同时,才会进行相等性比较。由于 a 和 b 的内存地址不同,因此不会进行相等性比较,从而避免了 RuntimeError。
为了解决这个问题,可以使用 torch.Tensor.size 属性,该属性是一个元组,表示张量的形状。可以创建一个字典,将不同形状的张量分别存储在不同的集合或列表中:
tensor_dict = {}
for tensor in [a, b]:
size = tuple(tensor.size())
if size not in tensor_dict:
tensor_dict[size] = set() # 或 list()
tensor_dict[size].add(tensor)
# 检查 b 是否存在于具有相同形状的张量集合中
size_b = tuple(b.size())
if size_b in tensor_dict and b in tensor_dict[size_b]:
print("b is in the collection")
else:
print("b is not in the collection")这种方法可以避免比较大小不同的张量,从而防止 RuntimeError。
in 操作符在 Python 中的行为取决于集合的类型。理解其内部实现机制对于编写高效且无错的代码至关重要。对于不使用哈希表的集合(如列表和元组),in 操作符会逐个比较元素。对于使用哈希表的集合(如集合和字典),in 操作符会首先比较哈希值,然后再进行相等性比较。在处理 PyTorch 张量时,需要注意大小不同的张量比较可能引发 RuntimeError。可以使用 torch.Tensor.size 属性来避免这种问题。
以上就是Python 中 in 操作符在集合与列表中的不同行为解析的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号