Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I understand why a tuple which contains a mutable object like list is not hashable, since list items in the tuple can still be updated.

example:

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

While this is not hashable:

# Not hashable

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

The above make sense to me.

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

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

and when I add a list:

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

Why this difference between tuples and namedtuples?

like image 785
MaverickD Avatar asked Oct 22 '18 00:10

MaverickD


1 Answers

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

You never do that. You create a named-tuple with a str, 'adam' and an int, 20

The following:

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

And

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

Do not create namedtuple objects, they create namedtuple classes. According to the docs:

Returns a new tuple subclass named typename.

In other words, collections.namedtuple is a factory function that returns a class. If you create instances of those classes, their instances follow the same rules as regular tuple instances.

So consider:

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

TestTuple, the return value of the namedtuple factory function, is not a namedtuple instance, it is an instance of type, like all other classes.

When you create instances of this class:

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

They follow the usual rules of hashability that regular tuple objects do:

>>> 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'

Note, the fieldnames argument accepts either a sequence (e.g. a list) of fieldnames, or for convenience, a space/comma-delimited string of fieldnames, so also from the docs:

... 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'.

like image 187
juanpa.arrivillaga Avatar answered Nov 15 '22 15:11

juanpa.arrivillaga