--- tags: python-course title: lesson-08 --- # Dictionaries [![hackmd-github-sync-badge](https://hackmd.io/_-6IcLirRS6WCnjqCm1smg/badge)](https://hackmd.io/_-6IcLirRS6WCnjqCm1smg) :::info :bulb: In this lesson students learn about the dictionary data structure and operations using dictionaries. :school: Teacher is Nina, student is James. ::: :::success :movie_camera: VIB background fading to course title slide. James and Ninas smiling faces appear. ::: :::warning :notes: Upbeat intro music ::: > scene 1 **James**: I can never remember the full names of amino acids. I would really like to have a program that I could just ask, "what's the full name of the amino acid with the single-letter-code, E?" and it would print out the name. Does that sound like it would be hard? **Nina**: I'm glad you recognise that it is possible. Now you're wondering how hard it would be. Well, perhaps you should start by just doing it to find out. **James**: Ok well, there are 20 amino acids so I can put the single-letter-codes into a list. But how can I get the full names from those? **Nina**: Good question. Maybe you can use another list? **James**: Ok. But how would I connect them? I suppose I could make sure they're in the same order. Then the index into the lists connect them. **Nina**: That sounds like a promising idea. But it is quite awkward. **Nina** (to the camera): Doing this is a useful exercise if you would like to try this for yourself now before you continue with this lesson. **Nina** (to James): Instead I'll tell you about a useful data structure that will make this trivial. It's called a "dictionary". Think of a real dictionary. When using a dictionary you look up a "key" in it and get a definition. In Python, your "key" would be the single-letter-code, and the definition would be the full name of the amino acid. **James**: Can you please show me how to make a dictionary? > [name=Bruna Piereck] It is silly, but I would show a 'cut' of a dictionary where they have word:meaning, even if it is one we make ourselfs, but that wold bring a concrete example and a subtil reference for the structure with ':' and then show the example nina describes. :::success :movie_camera: Animation of a dictionary definition ```python= { "A": "", "R": "", "N": "", "D": "", "C": "", "Q": "", "E": "", "G": "", "H": "", "I": "", "L": "", "K": "", "M": "", "F": "", "P": "", "S": "", "T": "", "W": "", "Y": "", "V": "" } ``` ::: > scene 2 **Nina**: This is a lot like a physical dictionary. In a dictionary you look up a word to find its definition. With a Python dictionary you look up a _key_ to find its _value_. And you do that with the index syntax, using square brackets. :::success :movie_camera: Animation of dictionary lookup ```python= >>> single_letter_codes["E"] 'Glutamate' ``` ::: > scene 3 **Nina**: Instead of putting a number between the brackets, you have to put a key in there. What do you think would happen if you try to look up a key that isn't in the dictionary? **James** (to the camera): I'm not sure. But I can guess... :::success :movie_camera: Animation of dictionary invalid key. ```python= >>> single_letter_codes["X"] KeyError: 'X' ``` ::: > scene 4 **Nina**: This is Python telling you that the key 'X' does not exist in the dictionary. **James**: But what if I need to lookup something in the dictionary but, when I'm writing the program, I don't know if the key exists in the dictionary or not? Like with lists, is there a way I can ask is a key is in the dictionary? **Nina** (over animation): Good question! You'd better avoid KeyError because it will teminate your program! You can check if a key exists by using the `in` operator just like with lists... :::success :movie_camera: Animation of `in` operator. ```python= >>> "E" in single_letter_codes True >>> "X" in single_letter_codes False if "X" in single_letter_codes: single_letter_codes["X"] else: "Not a known single-letter-code" ``` ::: > scene 5 "AAAAAGGGGGG" -> 5 Argininne, 6 Glutamine **James**: Alright, now I can safely look up keys in my translation dictionary. Sometimes I might want to generate a report, like, "here is a sequence, print each amino acids full name and the number of times it appears in the sequence". I think I can use my translation dictionary for this too. **Nina**: That's a good intuition, but you can use dictionaries for counting too! **James**: Oh! How? **Nina**: You could use the amino-acid single-letter-code as the key and the count as the value. **James**: I think I can do that. **Nina**: You're missing one important operation on dictionaries but why don't you try to do this first and you will find out what you're missing. :::success :movie_camera: Animation frequency counting initial attempt... ```python= def frequency(seq: str) -> dict[str, int]: pass ``` >[name=Bruna Piereck]I have the feeling we came to fast into this drequency issue. Maybe is worth adding one or two lines in how did you think of it, or how did you got into this. Not sure how, is just a feeling... In somepoint is good to discuss how are you geeting the frequences before you realise you don't know how to update the dictionary values. ::: > scene 6 **James**: Ok I've defined a function that takes a string as input and produces a dictionary as output. The output dictionary will have the bases as keys and their frequency in the input sequence as values. :::success :movie_camera: Animation frequency counting loop... ```python= def frequency(seq: str) -> dict[str, int]: output = {} # An empty dictionary for base in seq: output[base] # Update the frequency count ``` ::: > scene 7 **James**: Ok now I see what you mean. I don't know how to change the dictionary once I've defined it. **Nina**: Great! You've gone a long way by putting what you've learned so far into practice. You can update the dictionary by using the assignment operator to either add a new entry or to change a value of an existing key. :::success :movie_camera: Animation of a dictionary update ```python= >>> d = {} >>> d {} >>> d['A'] = 1 >>> d {'A': 1} >>> d['A'] = 2 {'A': 2} ``` ::: > scene 8 **James**: Ok I think I perfectly understand dictionaries! **Nina**: I'm going to test you then. How many keys does this dictionary contain? :::success :movie_camera: Dictionary with 2 keys ```python= >>> d = {"A": "Adenine", "A": "Adidas"} ``` ::: > scene 9 **James**: Ummm looks like there are 2 keys to me. **Nina**: And when you look up the key "A", what value would you get out? **James**: "Adenine"! No... oh I don't know. **Nina**: Yeah, keys in dictionaries need to be unique so you can look up a key and unambiguously get a value out. **James**: Ok sure. But that doesn't apply to values though right? **Nina**: No, values can be absolutely any Python value. There are no restrictions. **James**: Really anything? So I can use a dictionary as the value in another dictioary? **Nina**: Yes totally! **James**: Can a dictioany value be nothing? **Nina**: Yes, you would use the special `None` value, you can't just leave the place empty though (demo of syntax error). **James**: Ok I think I can write my reporting program now. **Nina**: Now you have all of the tools you need in order to count the frequency of bases in a sequence. **James**: Thanks Nina. Ok I think I see how I can apply this. Let me try. :::success :movie_camera: Animation frequency counting dict update... ```python= def frequency(seq: str) -> dict[str, int]: output = {} # An empty dictionary for base in seq: output[base] = output[base] + 1 ``` ::: > scene 10 **James**: There, I think that should work. **Nina**: Maybe, but you should really test it first. :::success :movie_camera: Animation frequency counting tests... ```python= def frequency(seq: str) -> dict[str, int]: output = {} # An empty dictionary for base in seq: output[base] = output[base] + 1 assert {"A": 1} == frequency("A"), "Expected {'A': 1}, got " + frequency("A") KeyError: 'A' ``` ::: > scene 11 **James** (panicing): What!? Why am I getting a key error? **Nina** (to the camera): Can you see James' mistake? **Nina** (to James): Does an empty dictionary have any keys you can access? **James**: No. Of course not. **Nina** (highlighting example): You're trying to access a key in the `output` dictionary that doesn't exist. It doesn't exist because there are no keys in the dictionary at all. **James**: So I need to check if the key is in there first... :::success :movie_camera: Animation frequency counting fix... ```python= def frequency(seq: str) -> dict[str, int]: output = {} # An empty dictionary for base in seq: if base in output: output[base] = output[base] + 1 else: output[base] = 1 assert {"A": 1} == frequency("A"), "Expected {'A': 1}, got " + frequency("A") ``` ::: > scene 12 **Nina**: That looks much better. **James**: And it passes my test! **Nina**: That's the end of this lesson. Can you tell me what you've learned? **James**: Thanks Nina! Ok let me see. I learned about the "dictionary" data type in this lesson which I believe is your favourite type? **Nina**: Yes it is! **James**: We also talked about some ways dictionaries are useful. When I have a association between data such as between amino-acid codes and their names, or dna bases and their frequencies in a sequence. Finally, I learned about how to use the `in` operator on dictionary keys and how to use the indexing and assignment operators to work with dictionaries. :::warning :notes: Upbeat outro music ::: :::success :movie_camera: Fade to VIB logo slide. :::