Python

Python Dictionary Example

Dictionaries in Python are data structures that optimize element lookups by associating keys to values. You could say that dictionaries are arrays of (key, value) pairs.

1. Define

Their syntax is pretty familiar to us, web developers, since its exactly the same as JSON’s. An object starts and ends in curly braces, each pair is separated by a comma, and the (key, value) pairs are associated through a colon (:). Let’s see:

base_example.py

EXAMPLE_DICT = {
    "animals": ["dog", "cat", "fish"],
    "a_number": 1,
    "a_name": "Sebastián",
    "a_boolean": True,
    "another_dict": {
        "you could": "keep going",
        "like this": "forever"
    }
}

Here, we are defining a dictionary which contains an array of animals, a number, a name, a boolean and another dictionary inside. This data structure is pretty versatile, as you can see. By the way, to define an empty dictionary you just write my_dict = {}.
 

2. Read

Now, let’s see how to retrieve a value from this dictionary. There are two ways of doing this: The known “[]” syntax where we write dict["key"] and get the result, or the dict.get("key", "default value") which also returns the result.

What’s the difference? Well, the [] syntax results in a KeyError if the key is not defined, and we usually don’t want that. The get method receives the key and an optional default value, and it does not result in any annoying error if the key is not found, it just returns None.

Let’s check out the [] syntax a little bit:

base_example.py

EXAMPLE_DICT = {
    "animals": ["dog", "cat", "fish"],
    "a_number": 1,
    "a_name": "Sebastián",
    "a_boolean": True,
    "another_dict": {
        "you could": "keep going",
        "like this": "forever"
    }
}

print(str(EXAMPLE_DICT["animals"]))  # outputs ['dog', 'cat', 'fish']
print(str(EXAMPLE_DICT["a_number"]))  # outputs 1
print(str(EXAMPLE_DICT["this_one_does_not_exist"]))  # throws KeyError

In every print statement you will find a comment predicting the result, anyway, let’s see the actual output:

['dog', 'cat', 'fish']
Traceback (most recent call last):
1
  File "/home/svinci/projects/jcg/dictionaries/base_example.py", line 14, in <module>
    print(str(EXAMPLE_DICT["this_one_does_not_exist"]))
KeyError: 'this_one_does_not_exist'

There it is, the key “this_one_does_not_exist” does not exist, so a KeyError is raised. Let’s put that KeyError in an except clause, and check the get method.

base_example.py

EXAMPLE_DICT = {
    "animals": ["dog", "cat", "fish"],
    "a_number": 1,
    "a_name": "Sebastián",
    "a_boolean": True,
    "another_dict": {
        "you could": "keep going",
        "like this": "forever"
    }
}

default_message = "oops, key not found"

print(str(EXAMPLE_DICT["animals"]))  # outputs ['dog', 'cat', 'fish']
print(str(EXAMPLE_DICT["a_number"]))  # outputs 1

try:
    print(str(EXAMPLE_DICT["this_one_does_not_exist"]))  # throws KeyError
except KeyError:
    print("KeyError")

print(str(EXAMPLE_DICT.get("animals", default_message)))  # outputs ['dog', 'cat', 'fish']
print(str(EXAMPLE_DICT.get("a_number", default_message)))  # outputs 1
print(str(EXAMPLE_DICT.get("this_one_does_not_exist")))  # outputs None
print(str(EXAMPLE_DICT.get("this_one_does_not_exist", default_message)))  # outputs "oops, key not found"

Here, we put the missing key in a try/except, which prints “KeyError” on failure. Then we see the get method examples, we ask for a key and return a default message if it’s not found. The output:

['dog', 'cat', 'fish']
1
KeyError
['dog', 'cat', 'fish']
1
None
oops, key not found

It’s pretty much the same behavior, but anything that doesn’t include a try/except is nicer.

3. Write

Dictionaries are mutable data structures. We can add keys to it, update their values and even delete them. Let’s see one by one in our example:

base_example.py

EXAMPLE_DICT = {
    "animals": ["dog", "cat", "fish"],
    "a_number": 1,
    "a_name": "Sebastián",
    "a_boolean": True,
    "another_dict": {
        "you could": "keep going",
        "like this": "forever"
    }
}


def print_key(dictionary, key):
    print(str(dictionary.get(key, "Key was not found")))

# Create a key
EXAMPLE_DICT["this_one_does_not_exist"] = "it exists now"  # This statement will create the key "this_one_does_not_exist" and assign to it the value "it exists now"
print_key(EXAMPLE_DICT, "this_one_does_not_exist")

# Update a key
EXAMPLE_DICT["a_boolean"] = False  # Exactly, it looks the same, and behaves the same. It just overwrites the given key.
print_key(EXAMPLE_DICT, "a_boolean")

# Delete a key
del EXAMPLE_DICT["this_one_does_not_exist"]  # Now "this_one_does_not_exist" ceased from existing (Again).
print_key(EXAMPLE_DICT, "this_one_does_not_exist")

There is a method called print_key which receives a dictionary and a key. It prints the value of the given key in the given dictionary or “Key was not found” instead.

We are creating the key this_one_does_not_exist first, and printing it out. Then we are updating a_boolean and also printing it. At last, we delete this_one_does_not_exist from the dict and print it again. The result:

it exists now
False
Key was not found

It works as expected. Notice that, in the context of writing a key, the [] syntax doesn’t throw an error if the key was not found, so in this case you can use it without worrying.

4. Useful operations

Python provides some useful operations to work with dictionaries, let’s see some of them:

4.1. In (keyword)

The in keyword is a tool Python provides to, in this case, check whether a key is present in a dictionary or not. Let’s make our own implementation of the get method to test this keyword:

dict_get.py

def get(dictionary, key, default_value=None):
    if key in dictionary:
        return dictionary[key]
    else:
        return default_value


my_dict = {
    "name": "Sebastián",
    "age": 21
}

print(str(get(my_dict, "name", "Name was not present")))
print(str(get(my_dict, "age", "Age was not present")))
print(str(get(my_dict, "address", "Address was not present")))

Withing our custom get method, we use the [] syntax to retrieve the requested key from the given dictionary, but not without checking if the key is present there with the in keyword. We receive a default value which by default is None. It behaves exactly like the native get method, here is the output:

Sebastián
21
Address was not present

This tool can also be used to iterate over the dictionary keys, let’s implement a method which returns an array with all the keys in a dictionary to see it working:

dict_all_keys.py

my_dict = {
    "name": "Sebastián",
    "age": 21
}


def keys(dictionary):
    return [k for k in dictionary]


print(str(keys(my_dict)))

It’s pretty simple, right? In human language, that iterates for every key in the given dictionary and returns an array with them. The output:

['name', 'age']

4.2. Len (built-in function)

The len function returns the number of key-value pairs in a dictionary. It’s data types do not matter in this context. Notice the fact that it returns the number of key-value pairs, so a dictionary that looks like {"key", "value"} will return 1 when asked for its length.

Its use is pretty simple, it receives the dictionary in question as argument, and just for testing purposes we’ll modify the dict_all_keys.py to check that the number of keys its returning is the same as the len of the dict.

dict_all_keys.py

my_dict = {
    "name": "Sebastián",
    "age": 21
}


def keys(dictionary):
    result = [k for k in dictionary]
    # result.append("break the program plz")
    if len(result) != len(dictionary):
        raise Exception('expected {} keys. got {}'.format(len(dictionary), len(result)))
    return result


print(str(keys(my_dict)))

There is the check, if we uncomment the second line of the function’s body we’ll see the exception raised:

Traceback (most recent call last):
  File "/home/svinci/projects/jcg/dictionaries/dict_all_keys.py", line 15, in <module<
    print(str(keys(my_dict)))
  File "/home/svinci/projects/jcg/dictionaries/dict_all_keys.py", line 11, in keys
    raise Exception('expected {} keys. got {}'.format(len(dictionary), len(result)))
Exception: expected 2 keys. got 3

The message Exception: expected 2 keys. got 3 is telling us that the keys we are about to return contain an extra key. This is another useless but explicit example.

4.3. keys() and values()

Let’s see a couple functions that render obsolete the function keys we just wrote. These are keys() and values(). I think it’s pretty intuitive what these functions do, keys returns every key in the dictionary and values returns every value in the dictionary.

Having a dictionary like me = {"name": "Sebastián", "age": 21}, me.keys() will return ["name", "age"] and me.values() will return ["Sebastián", 21]. Kind of… see, dictionaries in python are not ordered, then, the result of keys and values aren’t either. If you need to sort either keys or values just call sorted passing the array as argument.

Let’s see an example:

dict_keys_values.py

def print_dict(dictionary):
    ks = list(dictionary.keys())
    vs = list(dictionary.values())
    for i in range(0, len(ks)):
        k = ks[i]
        v = vs[i]
        print("{}: {}".format(str(k), str(v)))


example = {
    "name": "Sebastián",
    "last_name": "Vinci",
    "age": 21
}

print_dict(example)

Here we are creating a dictionary containing some keys, and then iterating over the keys and values to print them out. I know it’s not the prettiest code but it proves my point. When we execute it one time we see:

age: 21
name: Sebastián
last_name: Vinci

Then a second time:

last_name: Vinci
age: 21
name: Sebastián

See? That’s because dictionaries’ keys are not sorted, to solve this issue (and fix that awful piece of code) let’s change that script a little bit. We won’t be using the function values anymore since it’s not necessary, but I think it’s pretty clear the way to use it.

dict_keys_values.py

def print_dict(dictionary):
    ks = sorted(dictionary.keys())
    for k in ks:
        print("{}: {}".format(str(k), str(dictionary.get(k))))


example = {
    "name": "Sebastián",
    "last_name": "Vinci",
    "age": 21
}

print_dict(example)

Now we’ll always see the same output, as the keys are being sorted alphabetically. The output:

age: 21
last_name: Vinci
name: Sebastián

Now, why am I giving so much importance to the lack of sorting of dictionaries? Well, imagine you are storing it in a CSV file. Every time you write a dictionary, as the keys are in arbitrary orders, the CSV’s rows won’t be consistent, and it will mess up your program by writing each row with the columns in different orders. So when you need your data in a strict order, please keep in mind this little fact.

4.4. items()

This method returns a list of tuples containing every key-value pair in the dictionary as ({"a": 1, "b": 2}.items() => [("a", 1), ("b", 2)]). With this method we can iterate over this pair array more seamlessly (keep in mind that this is still unordered), so if we just want to print our dictionary and the order doesn’t matter we can write:

dict_items.py

def print_dict(dictionary):
    for k, v in dictionary.items():
        print("{}: {}".format(str(k), str(v)))


example = {
    "name": "Sebastián",
    "last_name": "Vinci",
    "age": 21
}

print_dict(example)

This will output the same as the first dict_keys_values.py, “key: value” with different orders in each execution. Something to notice is the fact that we can not assign new values to tuples. We access values in tuples by tuple[0] and tuple[1], but we can’t do tuepl[0] = 1. This will rase a TypeError: ‘tuple’ object does not support item assignment.

4.5. update()

This method changes one dictionary to have new values from a second one. It modifies existing values, as dictionaries are mutable (keep this in mind). Let’s see an example:

dict_update.py

product = {
    "description": "WCG E-Book",
    "price": 2.75,
    "sold": 1500,
    "stock": 5700
}


def sell(p):
    update = {"sold": p.get("sold", 0) + 1, "stock": p.get("stock") - 1}
    p.update(update)


def print_product(p):
    print(", ".join(["{}: {}".format(str(k), str(product[k])) for k in sorted(p.keys())]))

print_product(product)
for i in range(0, 100):
    sell(product)
print_product(product)

Here we are printing the product, executing sell a hundred times and then printing the product again. The sell function updates de product subtracting one from “stock” and adding one to “sold”. The output:

description: WCG E-Book, price: 2.75, sold: 1500, stock: 5700
description: WCG E-Book, price: 2.75, sold: 1600, stock: 5600

Notice that we don’t return a new dictionary with the updated values, it updates the existing one. This is an easy way of updating a couple keys in a dictionary without having to overwrite them one by one.

4.6. copy()

A dictionary’s copy method will copy the entire dictionary into a new one. One thing to notice is that it’s a shallow copy. If you have nested dictionaries, it will copy the first level, but the inner dictionaries will be a reference to the same object. Let’s see an example:

dict_copy.py

me = {
    "name": {
        "first": "Sebastián",
        "last": "Vinci"
    },
    "age": 21
}
my_clone = me.copy()

my_clone["age"] = 22
print("my age: {}, my clone's age: {}".format(me.get("age"), my_clone.get("age")))

my_clone.get("name")["first"] = "Michael"
print("my name: {}, my clone's name: {}".format(me.get("name").get("first"), my_clone.get("name").get("first")))

In this script, we are creating a dictionary with some data and then copying it. We change the clone’s age and print both, and then do the same with the first names.

Now, my clone is a copy of me, it is not a reference to the same memory address, so if I change its age, mine will remain the same. But, the inner dictionary, the one containing the name, is a reference to the same memory address, when I change my clone’s name, mine will change too. Let’s see the output:

my age: 21, my clone's age: 22
my name: Michael, my clone's name: Michael

So, having this in mind, we can use the copy, which, if used carefully, is a powerful and useful tool.

5. Download the Code Project

This was an basic example on Python Dictionaries.

Download
You can download the full source code of this example here: dictionaries

Sebastian Vinci

Sebastian is a full stack programmer, who has strong experience in Java and Scala enterprise web applications. He is currently studying Computers Science in UBA (University of Buenos Aires) and working a full time job at a .com company as a Semi-Senior developer, involving architectural design, implementation and monitoring. He also worked in automating processes (such as data base backups, building, deploying and monitoring applications).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button