Friday, July 5, 2024

8 Python Dictionary Things I Regret Not Knowing Earlier - Medium - Dictionary

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.

If You Wish To Support Me As A Creator

  • Buy my book! — 101 Things I Never Knew About Python
  • Where to find it: https://ift.tt/5qBW1Am
  • Clap 50 times for this story
  • Leave a comment telling me your thoughts
  • Highlight your favourite part of the story

Thank you! These tiny actions go a long way, and I really appreciate it!

YouTube: https://www.youtube.com/@zlliu246

LinkedIn: https://ift.tt/orTAv8q

Get an email whenever Liu Zuo Lin publishes.

Get an email whenever Liu Zuo Lin publishes. By signing up, you will create a Medium account if you don't already have…

zlliu.medium.com

Adblock test (Why?)

No comments:

Post a Comment