8 Python Dictionary Things I
Regret Not Knowing Earlier
Liu Zuo Lin
These tips have made dealing with dictionaries in Python a lot more enjoyable and
elegant, and I kinda wish I learnt them a little less late.
1) Creating a dictionary using dict(key=value)
Note — this is the way that my dev team creates dictionaries 95% of the time. We
don’t use {} very much.
# normal way of creating a dictionary
d = {'apple':4, 'orange':5, 'pear':6, 'pineapple':7}
# 'better' way to creating same dictionary
d = dict(apple=4, orange=5, pear=6, pineapple=7)
Why the better way is a better way:
when we use {}, we need to type the quote characters on string keys
for instance, 'apple' 'orange' and so on
having to type quote characters becomes exponentially annoying as we
have to deal with more and more keys
when we use dict(), we can ignore the quote characters
Of course, the dict() way doesn’t work with non-string keys, so both ways have
their uses.
2) Combining dicts using **
# here are 2 dicts
a = {1:1, 2:2}
b = {3:3, 4:4}
# we can combine them using **
x = {**a, **b}
print(x) # {1:1, 2:2, 3:3, 4:4}
the ** in front of the dictionaries unpacks the key-value pairs into the
parent dictionary
# we can add normal key-value pairs too
a = {1:1, 2:2}
b = {3:3, 4:4}
x = {**a, **b, 5:5}
print(x) # {1:1, 2:2, 3:3, 4:4, 5:5}
3) We can use ** to pass in a dict as keyword argumets
# a function that takes in a, b, c
def test(a, b, c):
print(a, b, c)
test(a=1, c=2, b=3) # 1 3 2
We can dynamically pass in a dictionary containing the keys a b and c into this
function too
mydict = dict(a=1, b=2, c=3)
print(mydict) # {'a':1, 'b':2, 'c':3}
# this is the same as test(a=1, b=2, c=3)
test(**mydict) # 1 2 3
^ the ** in front of the dict once again unpacks its key-value pairs into the
function test
Note — this is useful if we want to dynamically pass in keyword arguments into
functions.
4) Dictionary comprehension
Let’s say we want to create {1:1, 2:4, 3:9, 4:16}
# normal way to create this
d = {}
for i in range(1, 5):
d[i] = i**2
print(d) # {1: 1, 2: 4, 3: 9, 4: 16}
# dict comprehension way to create this
d = {i:i**2 for i in range(1, 5)}
print(d) # {1:1, 2:4, 3:9, 4:16}
Both are correct and legal ways. But notice that dict comprehension is so much
more elegant, Pythonic and easier to read.
# nested for loops
d = {}
for i in range(2):
for j in range(2, 4):
d[(i,j)] = 0
print(d)
# {(0, 2): 0, (0, 3): 0, (1, 2): 0, (1, 3): 0}
# nested for loops in dict comprehension
d = {(i,j):0 for i in range(2) for j in range(2, 4)}
print(d)
# {(0, 2): 0, (0, 3): 0, (1, 2): 0, (1, 3): 0}
5) dict.get(key, default_value)
When we access a non-existent key, we get KeyError
d = {1:1, 2:2, 3:3}
print(d[1]) # 1
print(d[4]) # KeyError
If we really don’t want a KeyError, we can use the .get() method instead, which
returns None if our key is non-existent.
# using .get()
d = {1:1, 2:2, 3:3}
print(d.get(1)) # 1
print(d.get(4)) # None
^ notice that instead of raising a KeyError, we get None instead
# .get() but with custom default value
d = {1:1, 2:2, 3:3}
print(d.get(1, 100)) # 1
print(d.get(4, 100)) # 100
print(d.get(9, 100)) # 100
^ we can define our custom default value too
6) Creating dict() using a list of tuples
# a list of tuples (of length 2)
ls = [('apple', 4), ('orange', 5), ('pear', 6)]
# we can pass this into dict() to create a dict
d = dict(ls)
print(d) # {'apple': 4, 'orange': 5, 'pear': 6}
^ this has been surprisingly useful in quickly creating dictionaries from tuples
without having to write dictionary comprehensions.
7) .items() and .values()
# a dict
d = dict(apple=4, orange=5, pear=6)
print(d) # {'apple':4, 'orange':5, 'pear':6}
When we iterate through the dict itself, we simply generate all dict keys:
for k in d:
print(k)
# apple
# orange
# pear
If we use .values(), we generate all dict values instead:
for v in d.values():
print(v)
# 4
# 5
# 6
If we use .items(), we generate both key and value as a tuple:
for k,v in d.items():
print(k, v)
# apple 4
# orange 5
# pear 6
^ I myself find .items() the most useful method here to quickly iterate through all
key-value pairs in a dictionary.
8) Stuff that can be dict keys, and stuff that cannot
In general:
immutable data types can be dict keys eg. int str tuple bool
mutable data types cannot eg. list dict
# attempt to use immutable data type as dict key
mylist = [1,2,3]
d = {mylist: 5}
# TypeError: unhashable type: 'list'
To legitimately check if some object can be used as a dict key, we can use the built-
in hash() function.
# using hash() on immutable data types
a: int = 4
b: str = 'hi'
print(hash(a)) # 4
print(hash(b)) # -4763374371541057969
# using hash() on mutable data types
l: list = [1, 2, 3]
d: dict = {1:1}
print(hash(l)) # TypeError: unhashable type: 'list'
print(hash(d)) # TypeError: unhashable type: 'dict'
So if you wish to create a custom object that can be a dictionary key, you can use
the __hash__ magic method to define how we want to hash our custom object.
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def __hash__(self):
return hash(str(self.name) + str(self.age))
dog1 = Dog('rocky', 4)
dog2 = Dog('fifi', 5)
d = {dog1: 1, dog2: 2}
print(d)
# {<__main__.Dog object at 0x10476a9f0>: 1, <__main__.Dog object at 0x10476aa20>: 2}
Conclusion
Hope this was clear and easy to understand.