Dict
Dictionaries can be thought of as a collection of key-value pairs or a named list of items. It used to be unordered, but recent Python versions ensure that the insertion order is maintained. See this tutorial for a more detailed discussion on dict
usage.
Initialization and accessing elements
A dict
data type is declared within {}
characters and each item requires two values — an immutable data type for key, followed by :
character and finally a value of any data type. The elements are separated by a comma character, just like the other container types.
To access an element, the syntax is dict_variable[key]
. Retrieving an item takes a constant amount of time, irrespective of the size of the dict
(see Hashtables for details). Dictionaries are mutable, so you can change an item's value, add items, remove items, etc.
>>> marks = {'Rahul': 86, 'Ravi': 92, 'Rohit': 75, 'Rajan': 79}
>>> marks['Rohit']
75
>>> marks['Rahul'] += 5
>>> marks['Ram'] = 67
>>> del marks['Rohit']
# note that the insertion order is maintained
>>> marks
{'Rahul': 91, 'Ravi': 92, 'Rajan': 79, 'Ram': 67}
Here's an example with list
and tuple
keys.
>>> list_key = {[1, 2]: 42}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
>>> items = {('car', 2): 'honda', ('car', 5): 'tesla', ('bike', 10): 'hero'}
>>> items[('bike', 10)]
'hero'
You can also use the dict() function for initialization in various ways. If all the keys are of str
data type, you can use the same syntax as keyword arguments seen earlier with function definitions. You can also pass a container type having two values per element, such as a list
of tuples
as shown below.
>>> marks = dict(Rahul=86, Ravi=92, Rohit=75, Rajan=79)
>>> marks
{'Rahul': 86, 'Ravi': 92, 'Rohit': 75, 'Rajan': 79}
>>> items = [('jeep', 20), ('car', 3), ('cycle', 5)]
>>> dict(items)
{'jeep': 20, 'car': 3, 'cycle': 5}
Another way to initialize is to use the fromkeys()
method that accepts an iterable and an optional value (default is None
). The same value will be assigned to all the keys, so be careful if you want to use a mutable object, since the same reference will be used as well.
>>> colors = ('red', 'blue', 'green')
>>> dict.fromkeys(colors)
{'red': None, 'blue': None, 'green': None}
>>> dict.fromkeys(colors, 255)
{'red': 255, 'blue': 255, 'green': 255}
get and setdefault
If you try to access a dict
key that doesn't exist, you'll get a KeyError
exception. If you do not want an exception to occur, you can use the get()
method. By default it'll return a None
value for keys that do not exist, which you can change by providing a default value as the second argument.
>>> marks = dict(Rahul=86, Ravi=92, Rohit=75, Rajan=79)
>>> marks['Ron']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'Ron'
>>> marks.get('Ravi')
92
>>> value = marks.get('Ron')
>>> print(value)
None
>>> marks.get('Ron', 0)
0
Here's a more practical example:
>>> vehicles = ['car', 'jeep', 'car', 'bike', 'bus', 'car', 'bike']
>>> hist = {}
>>> for v in vehicles:
... hist[v] = hist.get(v, 0) + 1
...
>>> hist
{'car': 3, 'jeep': 1, 'bike': 2, 'bus': 1}
Using get()
method will not automatically add keys that do not exist yet to the dict
object. You can use setdefault()
method, which behaves similarly to get()
except that keys will get created if not found. See also docs.python: collections.defaultdict.
>>> marks = dict(Rahul=86, Ravi=92, Rohit=75, Rajan=79)
>>> marks.get('Ram', 40)
40
>>> marks
{'Rahul': 86, 'Ravi': 92, 'Rohit': 75, 'Rajan': 79}
>>> marks.setdefault('Ram', 40)
40
>>> marks
{'Rahul': 86, 'Ravi': 92, 'Rohit': 75, 'Rajan': 79, 'Ram': 40}
Iteration
The default for
loop over a dict
object will give you a key for each iteration.
>>> fruits = dict(banana=12, papaya=5, mango=10, fig=100)
>>> for k in fruits:
... print(f'{k}:{fruits[k]}')
...
banana:12
papaya:5
mango:10
fig:100
# you'll also get only the keys if you apply list(), tuple() or set()
>>> list(fruits)
['banana', 'papaya', 'mango', 'fig']
As an exercise,
- given
fruits
dictionary as defined in the above code snippet, what do you think will happen when you usea, *b, c = fruits
? - given
nums = [1, 4, 6, 22, 3, 5, 4, 3, 6, 2, 1, 51, 3, 1]
, keep only first occurrences of a value from this list without changing the order of elements. You can do it withdict
features presented so far.[1, 4, 6, 22, 3, 5, 2, 51]
should be the output. See Using dict to eliminate duplicates while retaining order if you are not able to solve it.
Dict methods and operations
The in
operator checks if a key is present in the given dictionary. The keys()
method returns all the keys and values()
method returns all the values. These methods return a custom set-like object, but with insertion order maintained.
>>> marks = dict(Rahul=86, Ravi=92, Rohit=75, Rajan=79)
>>> 'Ravi' in marks
True
>>> 'Ram' in marks
False
>>> marks.keys()
dict_keys(['Rahul', 'Ravi', 'Rohit', 'Rajan'])
>>> marks.values()
dict_values([86, 92, 75, 79])
The items()
method can be used to get a key-value tuple
for each iteration.
>>> fruits = dict(banana=12, papaya=5, mango=10, fig=100)
# set-like object
>>> fruits.items()
dict_items([('banana', 12), ('papaya', 5), ('mango', 10), ('fig', 100)])
>>> for fruit, qty in fruits.items():
... print(f'{fruit}\t: {qty}')
...
banana : 12
papaya : 5
mango : 10
fig : 100
The del
statement example seen earlier removes the given key without returning the value associated with it. You can use the pop()
method to get the value as well. The popitem()
method removes the last added item and returns the key-value pair as a tuple
.
>>> marks = dict(Rahul=86, Ravi=92, Rohit=75, Rajan=79)
>>> marks.pop('Ravi')
92
>>> marks
{'Rahul': 86, 'Rohit': 75, 'Rajan': 79}
>>> marks.popitem()
('Rajan', 79)
>>> marks
{'Rahul': 86, 'Rohit': 75}
The update()
method allows you to add/update items from another dictionary or a container with key-value pair elements.
>>> marks = dict(Rahul=86, Ravi=92, Rohit=75, Rajan=79)
>>> marks.update(dict(Jo=89, Joe=75, Ravi=100))
# note that 'Ravi' has '100' as the updated value
>>> marks
{'Rahul': 86, 'Ravi': 100, 'Rohit': 75, 'Rajan': 79, 'Jo': 89, 'Joe': 75}
>>> fruits = dict(papaya=5, mango=10, fig=100)
>>> fruits.update([('tomato', 3), ('banana', 10)])
>>> fruits
{'papaya': 5, 'mango': 10, 'fig': 100, 'tomato': 3, 'banana': 10}
The |
operator is similar to the update()
method, except that you get a new dict
object instead of in-place modification.
>>> d1 = {'banana': 12, 'papaya': 5, 'mango': 20}
>>> d2 = {'mango': 10, 'fig': 100}
# this used to be done using unpacking, i.e. {**d1, **d2}
>>> d1 | d2
{'banana': 12, 'papaya': 5, 'mango': 10, 'fig': 100}
Arbitrary keyword arguments
To accept arbitrary number of keyword arguments, use **var_name
in the function definition. This has to be declared the last, after all the other types of arguments. Idiomatically, **kwargs
is used as the variable name. See stackoverflow: Decorators demystified for a practical use case of arbitrary keyword arguments.
>>> def many(**kwargs):
... print(f'{kwargs = }')
...
>>> many()
kwargs = {}
>>> many(num=5)
kwargs = {'num': 5}
>>> many(car=5, jeep=25)
kwargs = {'car': 5, 'jeep': 25}
Turning it around, when you have a function defined with keyword arguments, you can unpack a dictionary while calling the function.
>>> def greeting(phrase='hello', style='='):
... print(f'{phrase:{style}^{len(phrase)+6}}')
...
>>> greeting()
===hello===
>>> d = {'style': '-', 'phrase': 'have a nice day'}
>>> greeting(**d)
---have a nice day---