{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Data Structures\n", "\n", "The fundamental data types we have seen so far are useful for storing single values. However, in practice, we often need to work with collections of data. Python provides several built-in **collection types** to handle such cases. The most commonly used data structures in Python are\n", "\n", "- **Lists**: Ordered, mutable collections of items\n", "- **Tuples**: Ordered, immutable collections of items\n", "- **Dictionaries**: Ordered (Unordered prior to Python 3.7), mutable collections of key-value pairs\n", "- **Sets**: Unordered collections of unique items\n", "- **Ranges**: Immutable sequences of numbers, often used for iteration\n", "\n", "We will explore each of these types in more detail below.\n", "\n", "### Lists\n", "\n", "We have already seen lists in some of the previous examples. A list is an ordered collection of items that can be of different types. Lists are mutable, meaning you can change their contents after creation. You can create a list by enclosing items in square brackets `[]`, separated by commas." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.177683Z", "iopub.status.busy": "2026-01-19T18:20:26.177332Z", "iopub.status.idle": "2026-01-19T18:20:26.182630Z", "shell.execute_reply": "2026-01-19T18:20:26.182049Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2.5, 'Hello', True]\n" ] } ], "source": [ "my_list = [1, 2.5, \"Hello\", True]\n", "print(my_list)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can access individual elements in a list using their index, which starts at `0` for the first element. For example" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.224244Z", "iopub.status.busy": "2026-01-19T18:20:26.223976Z", "iopub.status.idle": "2026-01-19T18:20:26.227381Z", "shell.execute_reply": "2026-01-19T18:20:26.226740Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "First element: 1\n" ] } ], "source": [ "first_element = my_list[0]\n", "print(\"First element:\", first_element)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also access elements from the end of the list using negative indices, where `-1` refers to the last element, `-2` to the second last, and so on." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.229685Z", "iopub.status.busy": "2026-01-19T18:20:26.229453Z", "iopub.status.idle": "2026-01-19T18:20:26.232531Z", "shell.execute_reply": "2026-01-19T18:20:26.231963Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Last element: True\n" ] } ], "source": [ "last_element = my_list[-1]\n", "print(\"Last element:\", last_element)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Multiple elements can be accessed using slicing, which allows you to specify a range of indices. The syntax for slicing is `list[start:stop]`, where `start` is the index of the first element to include, and `stop` is the index of the first element to exclude." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.234890Z", "iopub.status.busy": "2026-01-19T18:20:26.234668Z", "iopub.status.idle": "2026-01-19T18:20:26.237808Z", "shell.execute_reply": "2026-01-19T18:20:26.237227Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Sub-list: [2.5, 'Hello']\n" ] } ], "source": [ "sub_list = my_list[1:3] # Elements at index 1 and 2\n", "print(\"Sub-list:\", sub_list)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since lists are mutable, you can modify their contents. For example, you can change the value of an element at a specific index." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.240158Z", "iopub.status.busy": "2026-01-19T18:20:26.239941Z", "iopub.status.idle": "2026-01-19T18:20:26.243144Z", "shell.execute_reply": "2026-01-19T18:20:26.242563Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "After modification: [1, 2.5, 'World', True]\n" ] } ], "source": [ "my_list[2] = \"World\"\n", "print(\"After modification:\", my_list)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To add elements to a list, we can use the `append()` method to add an item to the end of the list or the `insert()` method to add an item at a specific index, or `extend()` to add multiple items at once." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.245268Z", "iopub.status.busy": "2026-01-19T18:20:26.245063Z", "iopub.status.idle": "2026-01-19T18:20:26.247919Z", "shell.execute_reply": "2026-01-19T18:20:26.247493Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "After appending: [1, 2.5, 'World', True, 'New Item']\n" ] } ], "source": [ "my_list.append(\"New Item\")\n", "print(\"After appending:\", my_list)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.250031Z", "iopub.status.busy": "2026-01-19T18:20:26.249822Z", "iopub.status.idle": "2026-01-19T18:20:26.252718Z", "shell.execute_reply": "2026-01-19T18:20:26.252297Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "After inserting: [1, 'Inserted Item', 2.5, 'World', True, 'New Item']\n" ] } ], "source": [ "my_list.insert(1, \"Inserted Item\")\n", "print(\"After inserting:\", my_list)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.254867Z", "iopub.status.busy": "2026-01-19T18:20:26.254658Z", "iopub.status.idle": "2026-01-19T18:20:26.257469Z", "shell.execute_reply": "2026-01-19T18:20:26.257065Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "After extending: [1, 'Inserted Item', 2.5, 'World', True, 'New Item', 3, 4, 5]\n" ] } ], "source": [ "my_list.extend([3, 4, 5])\n", "print(\"After extending:\", my_list)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note how these methods modify the original list in place and return `None`, so you should not write `my_list = my_list.append(...)`.\n", "\n", "There are also options to remove items from a list. You can use the `remove()` method to remove the first occurrence of a specific value, the `pop()` method to remove an item at a specific index (or the last item if no index is provided), or the `clear()` method to remove all items from the list." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.259550Z", "iopub.status.busy": "2026-01-19T18:20:26.259344Z", "iopub.status.idle": "2026-01-19T18:20:26.262065Z", "shell.execute_reply": "2026-01-19T18:20:26.261640Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "After removing 'World': [1, 'Inserted Item', 2.5, True, 'New Item', 3, 4, 5]\n" ] } ], "source": [ "my_list.remove(\"World\")\n", "print(\"After removing 'World':\", my_list)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.264131Z", "iopub.status.busy": "2026-01-19T18:20:26.263932Z", "iopub.status.idle": "2026-01-19T18:20:26.266858Z", "shell.execute_reply": "2026-01-19T18:20:26.266411Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "After popping index 2: [1, 'Inserted Item', True, 'New Item', 3, 4, 5]\n", "Popped item: 2.5\n" ] } ], "source": [ "popped_item = my_list.pop(2) # Remove item at index 2\n", "print(\"After popping index 2:\", my_list)\n", "print(\"Popped item:\", popped_item)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.268900Z", "iopub.status.busy": "2026-01-19T18:20:26.268679Z", "iopub.status.idle": "2026-01-19T18:20:26.271350Z", "shell.execute_reply": "2026-01-19T18:20:26.270919Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "After clearing: []\n" ] } ], "source": [ "my_list.clear()\n", "print(\"After clearing:\", my_list)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is a convenient way to create lists using **list comprehensions**. List comprehensions provide a concise way to create lists based on existing iterables. The syntax is `[expression for item in iterable if condition]`, where `expression` is the value to be added to the list, `item` is the variable representing each element in the iterable, and `condition` is an optional filter. For example, here is how to create a list of squares of even numbers from `0` to `9`." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.273406Z", "iopub.status.busy": "2026-01-19T18:20:26.273220Z", "iopub.status.idle": "2026-01-19T18:20:26.276182Z", "shell.execute_reply": "2026-01-19T18:20:26.275728Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Squares of even numbers: [0, 4, 16, 36, 64]\n" ] } ], "source": [ "squares_of_even = [x**2 for x in range(10) if x % 2 == 0]\n", "print(\"Squares of even numbers:\", squares_of_even)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's break down the list comprehension above:\n", "\n", "- `x**2`: This is the expression that defines what each element in the new list will be. In this case, it's the square of `x`.\n", "- `for x in range(10)`: This part iterates over the numbers from `0` to `9`.\n", "- `if x % 2 == 0`: This is a condition that filters the numbers, including only even numbers in the new list. It uses the modulus operator `%` to check if `x` is divisible by `2`. If a number is divisible by `2`, the remainder is `0`, indicating that it is even.\n", "\n", "\n", "\n", "### Tuples\n", "\n", "Tuples are similar to lists in that they are ordered collections of items. However, tuples are immutable, meaning that once they are created, their contents cannot be changed. You can create a tuple by enclosing items in parentheses `()`, separated by commas." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.278135Z", "iopub.status.busy": "2026-01-19T18:20:26.277966Z", "iopub.status.idle": "2026-01-19T18:20:26.280725Z", "shell.execute_reply": "2026-01-19T18:20:26.280303Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(1, 2.5, 'Hello', True)\n" ] } ], "source": [ "my_tuple = (1, 2.5, \"Hello\", True)\n", "print(my_tuple)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can access elements in a tuple using indexing and slicing, just like with lists." ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.282612Z", "iopub.status.busy": "2026-01-19T18:20:26.282398Z", "iopub.status.idle": "2026-01-19T18:20:26.286286Z", "shell.execute_reply": "2026-01-19T18:20:26.285853Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "First element: 1\n", "Second element: 2.5\n", "Last element: True\n", "Sub-tuple: (2.5, 'Hello')\n" ] } ], "source": [ "first_element = my_tuple[0]\n", "print(\"First element:\", first_element)\n", "second_element = my_tuple[1]\n", "print(\"Second element:\", second_element)\n", "last_element = my_tuple[-1]\n", "print(\"Last element:\", last_element)\n", "sub_tuple = my_tuple[1:3] # Elements at index 1 and 2\n", "print(\"Sub-tuple:\", sub_tuple)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that we have seen tuples before when we defined functions that return multiple values. In such cases, Python automatically packs the returned values into a tuple, which can then be unpacked into separate variables." ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.289007Z", "iopub.status.busy": "2026-01-19T18:20:26.288816Z", "iopub.status.idle": "2026-01-19T18:20:26.291966Z", "shell.execute_reply": "2026-01-19T18:20:26.291582Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X coordinate: 10\n", "Y coordinate: 20\n" ] } ], "source": [ "def get_coordinates():\n", " x = 10\n", " y = 20\n", " return x, y # Returns a tuple (10, 20)\n", "\n", "x_coord, y_coord = get_coordinates() # Unpacks the tuple into separate variables\n", "print(\"X coordinate:\", x_coord)\n", "print(\"Y coordinate:\", y_coord)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that tuples are faster than lists for certain operations due to their immutability, making them a good choice for storing data that should not change. If you need to be able to modify the contents, use a list instead. For example, the following code will raise an error because we are trying to change an element of a tuple" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.293898Z", "iopub.status.busy": "2026-01-19T18:20:26.293727Z", "iopub.status.idle": "2026-01-19T18:20:26.295750Z", "shell.execute_reply": "2026-01-19T18:20:26.295302Z" } }, "outputs": [], "source": [ "#| error: true\n", "#my_tuple[1] = 3.0 # This will raise a TypeError" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "While tuples are immutable, you can concatenate two tuples to create a new tuple" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.297583Z", "iopub.status.busy": "2026-01-19T18:20:26.297409Z", "iopub.status.idle": "2026-01-19T18:20:26.300737Z", "shell.execute_reply": "2026-01-19T18:20:26.299995Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Combined tuple: (1, 2, 3, 4, 5, 6)\n" ] } ], "source": [ "tuple1 = (1, 2, 3)\n", "tuple2 = (4, 5, 6)\n", "combined_tuple = tuple1 + tuple2\n", "print(\"Combined tuple:\", combined_tuple)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "or you can repeat a tuple multiple times" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.302832Z", "iopub.status.busy": "2026-01-19T18:20:26.302614Z", "iopub.status.idle": "2026-01-19T18:20:26.305388Z", "shell.execute_reply": "2026-01-19T18:20:26.304895Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Repeated tuple: (1, 2, 3, 1, 2, 3, 1, 2, 3)\n" ] } ], "source": [ "repeated_tuple = tuple1 * 3\n", "print(\"Repeated tuple:\", repeated_tuple)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Unpacking can also be used with tuples. For example, you can unpack the elements of a tuple into separate variables" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.307567Z", "iopub.status.busy": "2026-01-19T18:20:26.307360Z", "iopub.status.idle": "2026-01-19T18:20:26.310314Z", "shell.execute_reply": "2026-01-19T18:20:26.309963Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a: 10\n", "b: 20\n", "c: 30\n" ] } ], "source": [ "my_tuple = (10, 20, 30)\n", "a, b, c = my_tuple\n", "print(\"a:\", a)\n", "print(\"b:\", b)\n", "print(\"c:\", c)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you don't want to unpack all elements, you can use the asterisk (`*`) operator to capture the remaining elements in a list" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.312237Z", "iopub.status.busy": "2026-01-19T18:20:26.312027Z", "iopub.status.idle": "2026-01-19T18:20:26.314966Z", "shell.execute_reply": "2026-01-19T18:20:26.314610Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a: 10\n", "b: 20\n", "rest: [30, 40, 50]\n" ] } ], "source": [ "my_tuple = (10, 20, 30, 40, 50)\n", "a, b, *rest = my_tuple\n", "print(\"a:\", a)\n", "print(\"b:\", b)\n", "print(\"rest:\", rest)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is also common to use `_` (underscore) as a variable name for values that you want to ignore during unpacking" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.317330Z", "iopub.status.busy": "2026-01-19T18:20:26.317043Z", "iopub.status.idle": "2026-01-19T18:20:26.319884Z", "shell.execute_reply": "2026-01-19T18:20:26.319511Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a: 10\n", "c: 30\n" ] } ], "source": [ "my_tuple = (10, 20, 30)\n", "a, _, c = my_tuple # Ignore the second element\n", "print(\"a:\", a)\n", "print(\"c:\", c)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Dictionaries\n", "\n", "Dictionaries are ordered (unordered prior to Python 3.7) collections of key-value pairs. Each key is unique and is used to access its corresponding value. Dictionaries are mutable, meaning you can change their contents after creation. The keys in a dictionary must be unique and immutable (e.g., strings, numbers, or tuples), while the values can be of any data type and can be duplicated. You can create a dictionary by enclosing key-value pairs in curly braces `{}`, with each key and value separated by a colon `:` and pairs separated by commas." ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.321950Z", "iopub.status.busy": "2026-01-19T18:20:26.321735Z", "iopub.status.idle": "2026-01-19T18:20:26.324395Z", "shell.execute_reply": "2026-01-19T18:20:26.324030Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'name': 'Alba', 'age': 30, 'is_student': False}\n" ] } ], "source": [ "my_dict = {\"name\": \"Alba\", \"age\": 30, \"is_student\": False}\n", "print(my_dict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can access values in a dictionary using their keys. For example" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.326478Z", "iopub.status.busy": "2026-01-19T18:20:26.326308Z", "iopub.status.idle": "2026-01-19T18:20:26.329356Z", "shell.execute_reply": "2026-01-19T18:20:26.328624Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Name: Alba\n" ] } ], "source": [ "name = my_dict[\"name\"]\n", "print(\"Name:\", name)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also add new key-value pairs or update existing ones" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.331287Z", "iopub.status.busy": "2026-01-19T18:20:26.331071Z", "iopub.status.idle": "2026-01-19T18:20:26.334239Z", "shell.execute_reply": "2026-01-19T18:20:26.333476Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "After adding city: {'name': 'Alba', 'age': 30, 'is_student': False, 'city': 'Madrid'}\n" ] } ], "source": [ "my_dict[\"city\"] = \"Madrid\" # Add a new key-value pair\n", "print(\"After adding city:\", my_dict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, you can use the `update()` method to add or update multiple key-value pairs at once" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.336315Z", "iopub.status.busy": "2026-01-19T18:20:26.336141Z", "iopub.status.idle": "2026-01-19T18:20:26.339637Z", "shell.execute_reply": "2026-01-19T18:20:26.338886Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "After updating age and adding country: {'name': 'Alba', 'age': 31, 'is_student': False, 'city': 'Madrid', 'country': 'Spain'}\n" ] } ], "source": [ "my_dict.update({\"age\": 31, \"country\": \"Spain\"})\n", "print(\"After updating age and adding country:\", my_dict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that if you use a key that already exists in the dictionary, the corresponding value will be updated. This applies whether you use the assignment syntax or the `update()` method.\n", "\n", "The keys and values can be accessed using the `keys()` and `values()` methods, respectively. You can also use the `items()` method to get key-value pairs as tuples." ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.341764Z", "iopub.status.busy": "2026-01-19T18:20:26.341525Z", "iopub.status.idle": "2026-01-19T18:20:26.344266Z", "shell.execute_reply": "2026-01-19T18:20:26.343864Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Keys: dict_keys(['name', 'age', 'is_student', 'city', 'country'])\n" ] } ], "source": [ "keys = my_dict.keys()\n", "print(\"Keys:\", keys)" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.346300Z", "iopub.status.busy": "2026-01-19T18:20:26.346091Z", "iopub.status.idle": "2026-01-19T18:20:26.348895Z", "shell.execute_reply": "2026-01-19T18:20:26.348454Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Values: dict_values(['Alba', 31, False, 'Madrid', 'Spain'])\n" ] } ], "source": [ "values = my_dict.values()\n", "print(\"Values:\", values)" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.351057Z", "iopub.status.busy": "2026-01-19T18:20:26.350878Z", "iopub.status.idle": "2026-01-19T18:20:26.353475Z", "shell.execute_reply": "2026-01-19T18:20:26.352996Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Items: dict_items([('name', 'Alba'), ('age', 31), ('is_student', False), ('city', 'Madrid'), ('country', 'Spain')])\n" ] } ], "source": [ "items = my_dict.items()\n", "print(\"Items:\", items)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The latter is particularly useful for iterating over both keys and values in a loop.\n", "\n", "We can remove key-value pairs from a dictionary using the `del` statement or the `pop()` method." ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.355513Z", "iopub.status.busy": "2026-01-19T18:20:26.355332Z", "iopub.status.idle": "2026-01-19T18:20:26.357790Z", "shell.execute_reply": "2026-01-19T18:20:26.357416Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "After deleting is_student: {'name': 'Alba', 'age': 31, 'city': 'Madrid', 'country': 'Spain'}\n" ] } ], "source": [ "del my_dict[\"is_student\"]\n", "print(\"After deleting is_student:\", my_dict)" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.359575Z", "iopub.status.busy": "2026-01-19T18:20:26.359400Z", "iopub.status.idle": "2026-01-19T18:20:26.362123Z", "shell.execute_reply": "2026-01-19T18:20:26.361610Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "After popping age: {'name': 'Alba', 'city': 'Madrid', 'country': 'Spain'}\n", "Popped age: 31\n" ] } ], "source": [ "age = my_dict.pop(\"age\")\n", "print(\"After popping age:\", my_dict)\n", "print(\"Popped age:\", age)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Sets\n", "\n", "Sets are unordered collections of unique items. They are mutable, meaning you can change their contents after creation. Sets are useful for storing items when the order does not matter and duplicates are not allowed. You can create a set by enclosing items in curly braces `{}`, separated by commas." ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.364046Z", "iopub.status.busy": "2026-01-19T18:20:26.363836Z", "iopub.status.idle": "2026-01-19T18:20:26.366751Z", "shell.execute_reply": "2026-01-19T18:20:26.366223Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Set: {1, 2, 3, 4, 5}\n" ] } ], "source": [ "my_set = {1, 2, 3, 4, 5}\n", "print(\"Set:\", my_set)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also create a set from an iterable, such as a list, using the `set()` constructor." ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.368902Z", "iopub.status.busy": "2026-01-19T18:20:26.368722Z", "iopub.status.idle": "2026-01-19T18:20:26.372145Z", "shell.execute_reply": "2026-01-19T18:20:26.371356Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Set from list: {1, 2, 3, 4, 5}\n" ] } ], "source": [ "my_list = [1, 2, 2, 3, 4, 4, 5]\n", "my_set_from_list = set(my_list)\n", "print(\"Set from list:\", my_set_from_list)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can add items to a set using the `add()` method and remove items using the `remove()` or `discard()` methods." ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.374418Z", "iopub.status.busy": "2026-01-19T18:20:26.374235Z", "iopub.status.idle": "2026-01-19T18:20:26.376738Z", "shell.execute_reply": "2026-01-19T18:20:26.376381Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "After adding 6: {1, 2, 3, 4, 5, 6}\n" ] } ], "source": [ "my_set.add(6)\n", "print(\"After adding 6:\", my_set)" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.378677Z", "iopub.status.busy": "2026-01-19T18:20:26.378434Z", "iopub.status.idle": "2026-01-19T18:20:26.381513Z", "shell.execute_reply": "2026-01-19T18:20:26.381080Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "After removing 3: {1, 2, 4, 5, 6}\n" ] } ], "source": [ "my_set.remove(3)\n", "print(\"After removing 3:\", my_set)" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.383884Z", "iopub.status.busy": "2026-01-19T18:20:26.383614Z", "iopub.status.idle": "2026-01-19T18:20:26.387024Z", "shell.execute_reply": "2026-01-19T18:20:26.386318Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "After discarding 10: {1, 2, 4, 5, 6}\n" ] } ], "source": [ "my_set.discard(10) # Does not raise an error if 10 is not in the set\n", "print(\"After discarding 10:\", my_set)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is also a `frozenset` type, which is an immutable version of a set. Once created, the contents of a frozenset cannot be changed. You can create a frozenset using the `frozenset()` constructor. " ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.389574Z", "iopub.status.busy": "2026-01-19T18:20:26.389344Z", "iopub.status.idle": "2026-01-19T18:20:26.392690Z", "shell.execute_reply": "2026-01-19T18:20:26.392028Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Frozenset: frozenset({1, 2, 3, 4, 5})\n" ] } ], "source": [ "my_frozenset = frozenset([1, 2, 3, 4, 5])\n", "print(\"Frozenset:\", my_frozenset)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sets are particularly useful for performing mathematical set operations such as union, intersection, difference, and symmetric difference. For example" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.395664Z", "iopub.status.busy": "2026-01-19T18:20:26.395365Z", "iopub.status.idle": "2026-01-19T18:20:26.398952Z", "shell.execute_reply": "2026-01-19T18:20:26.398356Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Union: {1, 2, 3, 4, 5, 6}\n" ] } ], "source": [ "set_a = {1, 2, 3, 4}\n", "set_b = {3, 4, 5, 6}\n", "union_set = set_a.union(set_b)\n", "print(\"Union:\", union_set)" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.401526Z", "iopub.status.busy": "2026-01-19T18:20:26.401266Z", "iopub.status.idle": "2026-01-19T18:20:26.404539Z", "shell.execute_reply": "2026-01-19T18:20:26.404001Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Intersection: {3, 4}\n" ] } ], "source": [ "intersection_set = set_a.intersection(set_b)\n", "print(\"Intersection:\", intersection_set)" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.406922Z", "iopub.status.busy": "2026-01-19T18:20:26.406693Z", "iopub.status.idle": "2026-01-19T18:20:26.410032Z", "shell.execute_reply": "2026-01-19T18:20:26.409386Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Difference (A - B): {1, 2}\n" ] } ], "source": [ "difference_set = set_a.difference(set_b)\n", "print(\"Difference (A - B):\", difference_set)" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.412269Z", "iopub.status.busy": "2026-01-19T18:20:26.412040Z", "iopub.status.idle": "2026-01-19T18:20:26.415114Z", "shell.execute_reply": "2026-01-19T18:20:26.414621Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Symmetric Difference: {1, 2, 5, 6}\n" ] } ], "source": [ "symmetric_difference_set = set_a.symmetric_difference(set_b)\n", "print(\"Symmetric Difference:\", symmetric_difference_set)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "More compactly, you can use operators for these operations" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.417646Z", "iopub.status.busy": "2026-01-19T18:20:26.417413Z", "iopub.status.idle": "2026-01-19T18:20:26.420531Z", "shell.execute_reply": "2026-01-19T18:20:26.419935Z" } }, "outputs": [], "source": [ "union_set = set_a | set_b\n", "intersection_set = set_a & set_b\n", "difference_set = set_a - set_b\n", "symmetric_difference_set = set_a ^ set_b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Ranges\n", "\n", "Ranges are immutable sequences of numbers, commonly used for iteration in loops. You can create a range using the `range()` function, which generates a sequence of numbers based on the specified start, stop, and step values. The syntax is `range(start, stop, step)`, where `start` is the first number in the sequence (inclusive), `stop` is the end of the sequence (exclusive), and `step` is the increment between each number." ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.422858Z", "iopub.status.busy": "2026-01-19T18:20:26.422627Z", "iopub.status.idle": "2026-01-19T18:20:26.425732Z", "shell.execute_reply": "2026-01-19T18:20:26.425263Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Range: [0, 2, 4, 6, 8]\n" ] } ], "source": [ "my_range = range(0, 10, 2) # Generates numbers from 0 to 8 with a step of 2\n", "print(\"Range:\", list(my_range)) # Convert to list for display" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also create a range with just the `stop` value, in which case the sequence starts from `0` and increments by `1` by default." ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.427973Z", "iopub.status.busy": "2026-01-19T18:20:26.427753Z", "iopub.status.idle": "2026-01-19T18:20:26.431016Z", "shell.execute_reply": "2026-01-19T18:20:26.430325Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Range with default start and step: [0, 1, 2, 3, 4]\n" ] } ], "source": [ "my_range_default = range(5) # Generates numbers from 0 to 4\n", "print(\"Range with default start and step:\", list(my_range_default))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You have seen earlier how to use ranges in `for` loops to iterate over a sequence of numbers. Ranges are memory efficient because they generate numbers on-the-fly and do not store the entire sequence in memory, making them suitable for large sequences.\n", "\n", "\n", "### Mutable vs. Immutable Objects\n", "\n", "In the examples up to now you have already seen that data types can be classified as either **mutable** or **immutable** based on whether their values can be changed after they are created.\n", "\n", "- **Mutable objects**: These objects can be modified after they are created. Examples of mutable data types in Python include lists, dictionaries, and sets. When you modify a mutable object, you are changing the object itself, and any other references to that object will reflect the changes.\n", "\n", "- **Immutable objects**: These objects cannot be modified after they are created. Examples of immutable data types in Python include integers, floats, strings, and tuples. When you attempt to modify an immutable object, you are actually creating a new object with the modified value, leaving the original object unchanged.\n", "\n", "An important implication of mutability is what happens when you assign one variable to another. For mutable objects, both variables will reference the same object in memory, so changes made through one variable will affect the other. For immutable objects, each variable will reference its own separate object." ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.433235Z", "iopub.status.busy": "2026-01-19T18:20:26.433017Z", "iopub.status.idle": "2026-01-19T18:20:26.436530Z", "shell.execute_reply": "2026-01-19T18:20:26.435885Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "list1: [1, 2, 3, 4]\n", "list2: [1, 2, 3, 4]\n" ] } ], "source": [ "# Mutable example with lists\n", "list1 = [1, 2, 3]\n", "list2 = list1 # Both variables reference the same list\n", "list2.append(4) # Modify list2\n", "print(\"list1:\", list1) # list1 is also affected\n", "print(\"list2:\", list2)" ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.438807Z", "iopub.status.busy": "2026-01-19T18:20:26.438578Z", "iopub.status.idle": "2026-01-19T18:20:26.441838Z", "shell.execute_reply": "2026-01-19T18:20:26.441342Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "str1: Hello\n", "str2: Hello, World!\n" ] } ], "source": [ "# Immutable example with strings\n", "str1 = \"Hello\"\n", "str2 = str1 # Both variables reference the same string\n", "str2 += \", World!\" # Modify str2 (creates a new string)\n", "print(\"str1:\", str1) # str1 remains unchanged\n", "print(\"str2:\", str2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The concept of mutability is important to understand when working with data structures and functions in Python, as it can affect how data is passed and modified within your code. When passing mutable objects to functions, changes made to the object within the function will affect the original object outside the function." ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.444045Z", "iopub.status.busy": "2026-01-19T18:20:26.443836Z", "iopub.status.idle": "2026-01-19T18:20:26.447156Z", "shell.execute_reply": "2026-01-19T18:20:26.446589Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2, 3, 100]\n" ] } ], "source": [ "def modify_list(input_list):\n", " input_list.append(100) # Modifies the original list\n", "\n", "my_list = [1, 2, 3]\n", "modify_list(my_list)\n", "print(my_list) # my_list is changed" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In contrast, passing immutable objects to functions will not affect the original object." ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "execution": { "iopub.execute_input": "2026-01-19T18:20:26.449332Z", "iopub.status.busy": "2026-01-19T18:20:26.449116Z", "iopub.status.idle": "2026-01-19T18:20:26.452559Z", "shell.execute_reply": "2026-01-19T18:20:26.451959Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5\n" ] } ], "source": [ "def modify_int(input_int):\n", " input_int += 10 # Creates a new integer\n", "\n", "my_int = 5\n", "modify_int(my_int)\n", "print(my_int) # my_int remains unchanged" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Therefore, it is crucial to be aware of the mutability of the data types you are working with to avoid unintended side effects in your code.\n", "\n", "\n", "### Exercises\n", "\n", "Now that we have covered the basics of data structures in Python, it's time to practice what we've learned. Here are some exercises to help you reinforce your understanding of lists, tuples, dictionaries, sets, and ranges.\n", "\n", "1. Create a list of the first 10 square numbers (i.e., 1, 4, 9, ..., 100) using a list comprehension. Print the resulting list.\n", "2. Create a tuple containing the names of the days of the week. Access and print the name of the third day.\n", "3. Create a dictionary that maps the names of three countries to their respective capitals. Access and print the capital of one of the countries.\n", "4. Create a set containing the unique vowels in the word \"programming\". Print the resulting set.\n", "5. Create a range of numbers from 1 to 20 with a step of 3. Use a for loop to iterate over the range and print each number." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3", "path": "/usr/local/share/jupyter/kernels/python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.5" } }, "nbformat": 4, "nbformat_minor": 4 }