为什么包含可变对象的 NamedTuple 可哈希,而包含可变对象的 Tuple 不可哈希?

Why is a NamedTuple containing mutable objects hashable, when a Tuple containing mutable objects is not?

我理解为什么包含像 list 这样的可变对象的 tuple 不可哈希,因为元组中的 list 项仍然可以更新。

示例:

# hashable
tuple_test = (1,2,3)
print(tuple_test.__hash__())

虽然这是不可哈希的:

# Not hashable

tuple_test2 = (1,2, [1,2])
print(tuple_test2.__hash__())

以上内容对我来说很有意义。

但是当我用 list 个项目创建一个 namedtuple 时,它仍然是可哈希的:

# hashable
named_tuple = namedtuple("TestTuple", 'name age')

当我添加 list:

# still hashable
named_tuple = namedtuple("TestTuple", ["name", "age"])
print(named_tuple(name="adam", age=20).__hash__())

为什么元组和命名元组之间存在这种差异?

But when I create a namedtuple with list as items it is still hashable...

你永远不会那样做。您使用 str'adam'int20

创建命名元组

以下:

named_tuple = namedtuple("TestTuple", 'name age')

named_tuple = namedtuple("TestTuple", ["name", "age"])

创建namedtuple对象,它们创建namedtupleclasses。根据 docs:

Returns a new tuple subclass named typename.

换句话说,collections.namedtuple是一个return是class的工厂函数。如果您创建这些 class 的实例,它们的实例将遵循与常规 tuple 实例相同的规则。

所以考虑:

>>> from collections import namedtuple
>>> TestTuple = namedtuple('TestTuple', ['name', 'age'])
>>> type(TestTuple)
<class 'type'>
>>> class A: pass
...
>>> type(A)
<class 'type'>

TestTuple,namedtuple工厂函数的return值,不是namedtuple实例,它是type的实例,像所有其他 classes.

当您创建此 class 的实例时:

>>> test_tuple = TestTuple('adam',32)
>>> type(test_tuple)
<class '__main__.TestTuple'>

它们遵循常规 tuple 对象所做的通常的散列性规则:

>>> hash(test_tuple)
5589201399616687819
>>> test_tuple = TestTuple('adam', [32, 31])
>>> hash(test_tuple)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

注意,fieldnames 参数接受字段名序列(例如列表),或者为方便起见,space/comma-delimited 字段名字符串,因此也来自文档:

... The field_names are a sequence of strings such as ['x', 'y']. Alternatively, field_names can be a single string with each fieldname separated by whitespace and/or commas, for example 'x y' or 'x, y'.