{"course":{"slug":"python-for-ai","title":"Python, from Zero, for AI","url":"https://addaly.com/learn/python-for-ai","level":"beginner","track":"build"},"lesson":{"slug":"lists-and-dicts","title":"Lists and dicts, and knowing which one you need","url":"https://addaly.com/learn/python-for-ai/lists-and-dicts","position":11,"of":89,"minutes":7,"answer":"A list is positions in order; a dict is named slots, and one name means exactly one slot.","keyIdea":"A list is positions in order; a dict is named slots, and one name means exactly one slot."},"body":{"format":"markdown","text":"## A list is things in a row\n\n```python\nprices = [120, 340, 90]\nprint(prices[0])      # 120\nprint(prices[2])      # 90\nprint(len(prices))    # 3\n\nprices.append(75)\nprint(prices)         # [120, 340, 90, 75]\nprint(sum(prices))    # 625\n```\n\nSquare brackets, commas between items. Positions start at zero, so the third item is `prices[2]`. This feels wrong for about a week and then feels normal.\n\nAsk for a position that does not exist and Python stops:\n\n```python\nprint(prices[9])\n```\n\n```\nIndexError: list index out of range\n```\n\nA list keeps its order, allows duplicates, and can be changed after it is made. `prices[0] = 130` replaces the first item.\n\n## A dict is things with labels\n\n```python\nstudent = {\"name\": \"Amara\", \"city\": \"Kano\", \"marks\": 78}\nprint(student[\"city\"])     # Kano\n\nstudent[\"marks\"] = 81      # change one\nstudent[\"year\"] = 2        # add a new one\nprint(student)\n```\n\nCurly braces, and each entry is a **key** and a **value** separated by a colon. You look things up by key, not by position. `student[0]` does not work here, because there is no position zero; there is a slot called `\"name\"`.\n\nAsk for a key that is not there and Python stops:\n\n```python\nprint(student[\"marks_2\"])\n```\n\n```\nKeyError: 'marks_2'\n```\n\nSafer, when you are not sure:\n\n```python\nprint(student.get(\"marks_2\"))          # None\nprint(student.get(\"marks_2\", 0))       # 0\nprint(\"city\" in student)               # True\n```\n\n## A key names one slot\n\nThis matters more than it looks:\n\n```python\nvotes = {\"yes\": 3, \"no\": 1, \"yes\": 5}\nprint(votes)        # {'yes': 5, 'no': 1}\nprint(len(votes))   # 2\n```\n\nWriting `\"yes\"` twice did not store two entries and did not raise an error. The second write landed in the same slot and replaced what was there. A dict is a set of named boxes, and a name refers to exactly one box.\n\n> Figure: Positions, or named slots. list: prices = [120, 90, 200]: Fetched by position: prices[2]; Order is part of the meaning; Duplicates are normal and kept; A position that does not exist raises IndexError; Right for: twelve months of rainfall, every turn in a conversation. dict: student = {\"name\": \"Asha\", …}: Fetched by key: student[\"city\"]; You never look things up by position; One key names one slot, so writing twice replaces; A key that does not exist raises KeyError, or .get gives a default; Right for: one student's name, city and marks. The honest answer to which one is usually both, nested: a list of turns, each turn a dict with a role and a content. That is the exact shape you will send to a model API, and you can already build it.\n\n## Choosing between them\n\nUse a **list** when the items are the same kind of thing and their order or count matters. Four prices. Twelve months of rainfall. Every message in a conversation.\n\nUse a **dict** when each piece has a different job and you will fetch it by name. One student's name, city and marks. One product's title, price and stock.\n\nThe honest answer to \"which one\" is usually: both, nested. This is the shape you will meet everywhere in AI work:\n\n```python\nmessages = [\n    {\"role\": \"user\", \"content\": \"Explain gravity in one sentence.\"},\n    {\"role\": \"assistant\", \"content\": \"Things with mass pull on each other.\"},\n    {\"role\": \"user\", \"content\": \"Now in Hindi.\"},\n]\n\nprint(len(messages))                 # 3\nprint(messages[0][\"content\"])        # Explain gravity in one sentence.\nprint(messages[-1][\"role\"])          # user\n```\n\nA list, because the order of a conversation is the whole point and there can be any number of turns. Dicts inside it, because a turn has two named parts. Read `messages[0][\"content\"]` left to right: take the list, take item zero, that is a dict, take its `\"content\"` slot.\n\n`messages[-1]` is the last item. Negative positions count from the end, which saves you writing `messages[len(messages) - 1]`.\n\n## Adding a turn\n\n```python\nmessages.append({\"role\": \"assistant\", \"content\": \"गुरुत्वाकर्षण...\"})\nprint(len(messages))   # 4\n```\n\nWhen you call an AI API in lesson ten, you will send almost exactly this structure. You already know how to build it.\n\n## Try this now\n\n```python\nbasket = [\n    {\"item\": \"rice\", \"price\": 850, \"qty\": 2},\n    {\"item\": \"oil\", \"price\": 1200, \"qty\": 1},\n]\nprint(basket[1][\"item\"])\nbasket[0][\"qty\"] = 3\nprint(basket[0])\n```"},"check":{"question":"You run `votes = {\"yes\": 3, \"no\": 1, \"yes\": 5}` and then `print(len(votes))` and `print(votes[\"yes\"])`. What do you get?","options":[{"text":"`3` and `3`, because a dict keeps every pair you write down, in the order you wrote them.","correct":false,"tempting":"You typed three pairs and Python accepted all three without complaint, so it is natural to expect all three to be in there."},{"text":"An error, because a dict requires its keys to be unique and repeating one is not allowed.","correct":false,"tempting":"Uniqueness of keys is a real rule, so it seems the language should enforce it by refusing the line rather than quietly picking a winner."},{"text":"`2` and `8`, because writing the same key again adds the new value to the value already stored.","correct":false,"tempting":"Counting votes is exactly the kind of task where you want totals, so an accumulating dict is what you were hoping for."},{"text":"`2` and `5`, because `\"yes\"` names one slot and the later write replaced what was in it.","correct":true}]},"prev":{"slug":"how-python-runs-code","title":"What happens between your file and the answer","url":"https://addaly.com/learn/python-for-ai/how-python-runs-code"},"next":{"slug":"indexing-and-slicing","title":"Reaching into a sequence: indexes, slices and why the end is excluded","url":"https://addaly.com/learn/python-for-ai/indexing-and-slicing"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'Lists and dicts, and knowing which one you need', in Python, from Zero, for AI. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/python-for-ai/lists-and-dicts (Accessed: 31 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"Lists and dicts, and knowing which one you need","courseTitle":"Python, from Zero, for AI","module":{"index":2,"slug":"collections-and-loops","title":"Collections, loops, and the shape of your data"},"answer":null,"keyIdea":"A list is positions in order; a dict is named slots, and one name means exactly one slot.","hero":{"kind":"figure","figure":{"t":"figure","label":"Figure 2.1","figure":{"left":{"title":"list: prices = [120, 90, 200]","items":["Fetched by position: prices[2]","Order is part of the meaning","Duplicates are normal and kept","A position that does not exist raises IndexError","Right for: twelve months of rainfall, every turn in a conversation"]},"type":"compare","right":{"title":"dict: student = {\"name\": \"Asha\", …}","items":["Fetched by key: student[\"city\"]","You never look things up by position","One key names one slot, so writing twice replaces","A key that does not exist raises KeyError, or .get gives a default","Right for: one student’s name, city and marks"]},"title":"Positions, or named slots","caption":"The honest answer to which one is usually both, nested: a list of turns, each turn a dict with a role and a content. That is the exact shape you will send to a model API, and you can already build it."},"alt":"Figure: Positions, or named slots. list: prices = [120, 90, 200]: Fetched by position: prices[2]; Order is part of the meaning; Duplicates are normal and kept; A position that does not exist raises IndexError; Right for: twelve months of rainfall, every turn in a conversation. dict: student = {\"name\": \"Asha\", …}: Fetched by key: student[\"city\"]; You never look things up by position; One key names one slot, so writing twice replaces; A key that does not exist raises KeyError, or .get gives a default; Right for: one student’s name, city and marks. The honest answer to which one is usually both, nested: a list of turns, each turn a dict with a role and a content. That is the exact shape you will send to a model API, and you can already build it."}},"blocks":[{"t":"h2","id":"a-list-is-things-in-a-row","runs":[{"text":"A list is things in a row"}]},{"t":"code","lang":"python","label":"Python","text":"prices = [120, 340, 90]\nprint(prices[0])      # 120\nprint(prices[2])      # 90\nprint(len(prices))    # 3\n\nprices.append(75)\nprint(prices)         # [120, 340, 90, 75]\nprint(sum(prices))    # 625"},{"t":"p","runs":[{"text":"Square brackets, commas between items. Positions start at zero, so the third item is "},{"text":"prices[2]","code":true},{"text":". This feels wrong for about a week and then feels normal."}]},{"t":"p","runs":[{"text":"Ask for a position that does not exist and Python stops:"}]},{"t":"code","lang":"python","label":"Python","text":"print(prices[9])"},{"t":"code","lang":"","label":"Text","text":"IndexError: list index out of range"},{"t":"p","runs":[{"text":"A list keeps its order, allows duplicates, and can be changed after it is made. "},{"text":"prices[0] = 130","code":true},{"text":" replaces the first item."}]},{"t":"h2","id":"a-dict-is-things-with-labels","runs":[{"text":"A dict is things with labels"}]},{"t":"code","lang":"python","label":"Python","text":"student = {\"name\": \"Amara\", \"city\": \"Kano\", \"marks\": 78}\nprint(student[\"city\"])     # Kano\n\nstudent[\"marks\"] = 81      # change one\nstudent[\"year\"] = 2        # add a new one\nprint(student)"},{"t":"p","runs":[{"text":"Curly braces, and each entry is a "},{"text":"key","b":true},{"text":" and a "},{"text":"value","b":true},{"text":" separated by a colon. You look things up by key, not by position. "},{"text":"student[0]","code":true},{"text":" does not work here, because there is no position zero; there is a slot called "},{"text":"\"name\"","code":true},{"text":"."}]},{"t":"p","runs":[{"text":"Ask for a key that is not there and Python stops:"}]},{"t":"code","lang":"python","label":"Python","text":"print(student[\"marks_2\"])"},{"t":"code","lang":"","label":"Text","text":"KeyError: 'marks_2'"},{"t":"p","runs":[{"text":"Safer, when you are not sure:"}]},{"t":"code","lang":"python","label":"Python","text":"print(student.get(\"marks_2\"))          # None\nprint(student.get(\"marks_2\", 0))       # 0\nprint(\"city\" in student)               # True"},{"t":"h2","id":"a-key-names-one-slot","runs":[{"text":"A key names one slot"}]},{"t":"p","runs":[{"text":"This matters more than it looks:"}]},{"t":"code","lang":"python","label":"Python","text":"votes = {\"yes\": 3, \"no\": 1, \"yes\": 5}\nprint(votes)        # {'yes': 5, 'no': 1}\nprint(len(votes))   # 2"},{"t":"p","runs":[{"text":"Writing "},{"text":"\"yes\"","code":true},{"text":" twice did not store two entries and did not raise an error. The second write landed in the same slot and replaced what was there. A dict is a set of named boxes, and a name refers to exactly one box."}]},{"t":"h2","id":"choosing-between-them","runs":[{"text":"Choosing between them"}]},{"t":"p","runs":[{"text":"Use a "},{"text":"list","b":true},{"text":" when the items are the same kind of thing and their order or count matters. Four prices. Twelve months of rainfall. Every message in a conversation."}]},{"t":"p","runs":[{"text":"Use a "},{"text":"dict","b":true},{"text":" when each piece has a different job and you will fetch it by name. One student’s name, city and marks. One product’s title, price and stock."}]},{"t":"p","runs":[{"text":"The honest answer to “which one” is usually: both, nested. This is the shape you will meet everywhere in AI work:"}]},{"t":"code","lang":"python","label":"Python","text":"messages = [\n    {\"role\": \"user\", \"content\": \"Explain gravity in one sentence.\"},\n    {\"role\": \"assistant\", \"content\": \"Things with mass pull on each other.\"},\n    {\"role\": \"user\", \"content\": \"Now in Hindi.\"},\n]\n\nprint(len(messages))                 # 3\nprint(messages[0][\"content\"])        # Explain gravity in one sentence.\nprint(messages[-1][\"role\"])          # user"},{"t":"p","runs":[{"text":"A list, because the order of a conversation is the whole point and there can be any number of turns. Dicts inside it, because a turn has two named parts. Read "},{"text":"messages[0][\"content\"]","code":true},{"text":" left to right: take the list, take item zero, that is a dict, take its "},{"text":"\"content\"","code":true},{"text":" slot."}]},{"t":"p","runs":[{"text":"messages[-1]","code":true},{"text":" is the last item. Negative positions count from the end, which saves you writing "},{"text":"messages[len(messages) - 1]","code":true},{"text":"."}]},{"t":"h2","id":"adding-a-turn","runs":[{"text":"Adding a turn"}]},{"t":"code","lang":"python","label":"Python","text":"messages.append({\"role\": \"assistant\", \"content\": \"गुरुत्वाकर्षण...\"})\nprint(len(messages))   # 4"},{"t":"p","runs":[{"text":"When you call an AI API in lesson ten, you will send almost exactly this structure. You already know how to build it."}]},{"t":"h2","id":"try-this-now","runs":[{"text":"Try this now"}],"role":"try"},{"t":"code","lang":"python","label":"Python","text":"basket = [\n    {\"item\": \"rice\", \"price\": 850, \"qty\": 2},\n    {\"item\": \"oil\", \"price\": 1200, \"qty\": 1},\n]\nprint(basket[1][\"item\"])\nbasket[0][\"qty\"] = 3\nprint(basket[0])"}],"check":{"question":[{"text":"You run "},{"text":"votes = {\"yes\": 3, \"no\": 1, \"yes\": 5}","code":true},{"text":" and then "},{"text":"print(len(votes))","code":true},{"text":" and "},{"text":"print(votes[\"yes\"])","code":true},{"text":". What do you get?"}],"options":[{"text":[{"text":"3","code":true},{"text":" and "},{"text":"3","code":true},{"text":", because a dict keeps every pair you write down, in the order you wrote them."}],"correct":false,"tempting":[{"text":"You typed three pairs and Python accepted all three without complaint, so it is natural to expect all three to be in there."}]},{"text":[{"text":"An error, because a dict requires its keys to be unique and repeating one is not allowed."}],"correct":false,"tempting":[{"text":"Uniqueness of keys is a real rule, so it seems the language should enforce it by refusing the line rather than quietly picking a winner."}]},{"text":[{"text":"2","code":true},{"text":" and "},{"text":"8","code":true},{"text":", because writing the same key again adds the new value to the value already stored."}],"correct":false,"tempting":[{"text":"Counting votes is exactly the kind of task where you want totals, so an accumulating dict is what you were hoping for."}]},{"text":[{"text":"2","code":true},{"text":" and "},{"text":"5","code":true},{"text":", because "},{"text":"\"yes\"","code":true},{"text":" names one slot and the later write replaced what was in it."}],"correct":true,"tempting":null}],"order":[0,3,2,1],"why":null},"prev":{"slug":"how-python-runs-code","title":"What happens between your file and the answer","minutes":8},"next":{"slug":"indexing-and-slicing","title":"Reaching into a sequence: indexes, slices and why the end is excluded","minutes":8}}}