
本文深入探讨python中`in`运算符在列表、集合和字典中的不同工作原理,并解析对象相等性(`__eq__`)与哈希值(`__hash__`)之间的关键契约。通过polars `dtype`的实际案例,揭示当此契约被违反时,哈希集合成员判断为何会产生非预期结果。文章阐明了polars `dtype`独特的设计考量,包括其非传递性和哈希不一致性,旨在为开发者在使用哈希集合时提供专业指导。
在Python中,in 运算符用于判断一个元素是否属于某个集合。然而,它在不同类型的集合中有着不同的底层实现机制,这直接影响了其性能和行为。
在列表 (list) 中: 当使用 x in [a, b, c] 时,Python会进行线性搜索。它会遍历列表中的每一个元素,并使用对象的相等性比较 (__eq__ 方法) 来检查 x 是否与列表中的某个元素相等。如果找到一个相等的元素,则返回 True;否则,遍历完整个列表后返回 False。
在集合 (set) 或字典 (dict) 中: 当使用 x in {a, b, c} (对于集合) 或 x in {a: 1, b: 2} (对于字典的键) 时,Python会采用哈希查找机制。这种机制依赖于对象的哈希值 (__hash__ 方法)。
为了确保哈希查找机制的正确性和一致性,Python对自定义类在实现 __eq__ (相等性比较) 和 __hash__ (哈希值计算) 方法时,有一个严格的契约:
核心契约: 如果两个对象被认为是相等的(即 a == b 返回 True),那么它们必须拥有相同的哈希值(即 hash(a) == hash(b) 必须返回 True)。
如果一个类重写了 __eq__ 方法但没有正确重写 __hash__ 方法以遵守此契约,或者将其设置为不可哈希(通过将 __hash__ 设置为 None),那么当这些对象的实例被用作哈希集合(如 set 的元素或 dict 的键)时,就可能出现非预期的行为。
立即学习“Python免费学习笔记(深入)”;
Polars 是一个高性能的数据帧库,其 dtype (数据类型) 对象在相等性判断和哈希行为上展现出一些特殊性,这恰好是上述Python契约的一个典型案例。考虑以下Polars代码示例:
import polars as pl
s = pl.Series(["a", "b"], dtype=pl.Categorical)
print(f"s.dtype is pl.Categorical: {s.dtype is pl.Categorical}")
print(f"s.dtype == pl.Categorical: {s.dtype == pl.Categorical}")
print(f"hash(s.dtype) == hash(pl.Categorical): {hash(s.dtype) == hash(pl.Categorical)}")运行上述代码,你会得到如下输出:
s.dtype is pl.Categorical: False s.dtype == pl.Categorical: True hash(s.dtype) == hash(pl.Categorical): False
从输出可以看出:
这违反了Python关于 __eq__ 和 __hash__ 的核心契约。
正是因为哈希值的不一致,导致了 in 运算符在列表和哈希集合中行为的差异:
import polars as pl
s = pl.Series(["a", "b"], dtype=pl.Categorical)
# 在列表中查找:基于线性遍历和 __eq__
print(f"s.dtype in [pl.Categorical, pl.Enum]: {s.dtype in [pl.Categorical, pl.Enum]}") # True
# 在集合中查找:基于哈希值和 __eq__
print(f"s.dtype in {{pl.Categorical, pl.Enum}}: {s.dtype in {pl.Categorical, pl.Enum}}") # False
# 在字典键中查找:基于哈希值和 __eq__
print(f"s.dtype in {{pl.Categorical: 1, pl.Enum: 2}}: {s.dtype in {pl.Categorical: 1, pl.Enum: 2}}") # False值得注意的是,Polars dtype 的这种行为是经过深思熟虑的设计选择,而非一个简单的错误。Polars团队在相关讨论中指出,其数据类型对象不遵循常规的相等性契约,具体表现在:
非传递性: 某些特定的数据类型可能被认为与更通用的数据类型相等,但不同特定的数据类型之间却不相等。例如:
哈希不一致性: 正如我们所见,即使 a == b 为 True,hash(a) 也可能不等于 hash(b)。
这种设计允许Polars在内部处理数据类型时具有更大的灵活性,尤其是在类型推断和兼容性检查方面。然而,这也意味着开发者在使用Polars dtype 对象时,需要特别注意它们在标准Python哈希集合中的行为。
if s.dtype in [pl.Categorical, pl.Enum]:
print("dtype is Categorical or Enum (using list)")Python的 in 运算符在列表和哈希集合(set、dict)中有着根本不同的实现机制。列表进行线性遍历和 __eq__ 比较,而哈希集合则依赖于对象的哈希值 (__hash__) 进行快速查找,并辅以 __eq__ 确认。Python对 __eq__ 和 __hash__ 之间存在一个严格的契约:如果 a == b 为真,则 hash(a) 必须等于 hash(b)。
Polars的 dtype 对象由于其独特的设计,违反了这一契约,导致相等的 dtype 实例可能拥有不同的哈希值。这使得在 set 或 dict 中使用 in 运算符检查 Polars dtype 的成员资格时,会产生非预期的 False 结果。因此,在处理Polars dtype 时,建议优先使用列表进行成员判断,并深刻理解Python对象相等性与哈希值的核心契约,这对于编写健壮和可预测的代码至关重要。
以上就是深入理解Python in 运算符与Polars dtype的相等性及哈希行为的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号