Why in Python can we define an index of a Dict as a Tuple?

Asked

Viewed 392 times

5

I noticed that in Python, we can add values like tuple as an index of a dict (dictionary).

Example:

test = {}

test[(1, 2, 3)] = ("um", "dois", "tres")

print test // {(1, 2, 3): ('um', 'dois', 'tres')}

And after the definition, how do we access the value for the key that we define as tuple?

And what is the purpose of it?

  • I have this doubt because I come from PHP, and, "there", you cannot define an index of an array as another type that is not int or string (nor does float accept).

  • 1

    Beware that Python dictionaries are quite different from "arrays" in PHP - "arrays" in PHP are a type of wildcard object, which works both as a sequence, with numerical inidces and as a mapping with string keys. They are separate concepts, which in Pyrhon are well separated - the most common types are lists and tuples for sequences, and dictionaries for mapping.

3 answers

3

To access, we only use the tuple as key, following his example:

print test[(1,2,3)] #Isso retornará ('um', 'dois', 'tres')

The usefulness of this will depend on its implementation. For example, imagine a database that a certain set of values of a line give me a value, I could use the tuple for this purpose to access a specific value.

2


Python dictionaries do not have to have restrictions on how data structures are implemented in other languages.

The constraints for an object to be used as a dictionary key are: the object must implement a method __hash__ that returns a unique and constant value; the object has equality comparison with other objects (__eq__) - and that neither this hash nor the equality condition varies as long as the object is a dictionary key. In general, to simplify we say that immutable objects can be dictionary keys - but by following these constraints even mutable objects can serve.

The "what the utility" varies enormously: it depends on what you want to do - tuples in general are practical because they let you use a dictionary as a sparse matrix, face - just use the "get" method to pick up values:

W = H = 10
a = {}
a[5,5] = 1
print ("\n".join(", ".join(str(a.get((x,y), 0))  for x in range(W))  for y in range(H)) )

In this example, I use the index in the method get why it allows specifying a default value for when the key does not exist - (which happens for all combinations of x and y, except for (5,5) that I declared). The exit is:

0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 1, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0

Tuples are exceptionally practical because the surrounding parentheses can be omitted when used in an unambiguous context - as indexes within brackets (see example above).

But objects that represent date and time, with rich comparisons, frozen sets (Frozen sets), even functions can be used as dictionary keys (in log applications, for example, that count the number of times a function is called, or that cache) - it is legal not to have an artificial restriction.

A context in which different keys should be taken care of is in applications that have to serialize the data as JSON, for example, Rest Apis, or in calls to some non-relational databases: the JSON standard requires that dcionary chavves be strings.

  • 2

    +1, but it is not true that the keys need to have the method __hash__ - this is only true if the key has the method __eq__ also; if she has neither, you can use the dictionary, but the comparison is made by id of the object: {object(): object() for i in range(10)} is a dict with 10 entries.

  • 1

    True - but the dictionary implementation continues using the object hash - is that if it does not have a __hash__, value is based on ID.

1

Complementing the other responses, the numpy (a library to do matrix operations) abuses this fact to allow you to do block operations:

  • if M is a numpy.array, M[5:10, 10:15] is a 5 5 5 matrix consisting of the elements at the intersection of the 5th to 9th lines of M with the 10th to 14th column of M;
  • if L is a one-dimensional vector, L[numpy.array([1, 1, 2, 3, 5, 8, 13, 21])] is a vector consisting only of the elements at the positions of the Fibonacci sequence;
  • if N is any matrix, N[N < 0] = -N assigns to N the absolute value of each of its entries.

To documentation of numpy has more creative abuse of Python indexing.

(Yes, it is true that the numpy.array is not a dictionary, but an object that overloads its method __getitem__ to allow M[…], but I think this is a good example of why it is useful to allow arbitrary objects between brackets.)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.