{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "ULxWGdhVqcRt" }, "source": [ "# Chapter 8: Extra Content\n", "\n", "```{image} abertura-light.png\n", ":alt: Horizontal lines that break off at different points and fall until a double rule at the foot catches them\n", ":class: abertura only-light\n", "```\n", "\n", "```{image} abertura-dark.png\n", ":alt: Horizontal lines that break off at different points and fall until a double rule at the foot catches them\n", ":class: abertura only-dark\n", "```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Error Handling (`try/except`)\n", "\n", "In Python, we use the `try/except` block to handle errors that may occur during program execution, preventing it from being interrupted unexpectedly.\n", "\n", "**Basic example with `try/except`:**\n", "\n", "```python\n", "try:\n", " number = int(input(\"Enter a number: \"))\n", " result = 10 / number\n", " print(f\"The result is {result}.\")\n", "except ZeroDivisionError:\n", " print(\"Error: division by zero.\")\n", "except ValueError:\n", " print(\"Error: invalid input.\")\n", "```\n", "\n", "**Output:**\n", "\n", "```\n", "Enter a number: 2\n", "The result is 5.0.\n", "```\n", "\n", "```\n", "Enter a number: 0\n", "Error: division by zero.\n", "```\n", "\n", "```\n", "Enter a number: abc\n", "Error: invalid input.\n", "```\n", "\n", "\n", "### `else` and `finally` blocks in error handling\n", "\n", "In addition to `try` and `except`, Python allows the optional use of `else` and `finally` blocks to make error handling more complete.\n", "\n", "**`else`**\n", "\n", "Executed **only if no error occurs** in the `try` block.\n", "\n", "**`finally`**\n", "\n", "Executed **always**, with or without an error. It is useful for releasing resources, such as closing files or database connections.\n", "\n", "**Example with `else` and `finally`:**\n", "\n", "```python\n", "try:\n", " number = int(input(\"Enter a number: \"))\n", " result = 10 / number\n", "except ZeroDivisionError:\n", " print(\"Error: division by zero.\")\n", "except ValueError:\n", " print(\"Error: invalid input.\")\n", "else:\n", " print(f\"The result is {result}.\")\n", "finally:\n", " print(\"Execution finished.\")\n", "```\n", "\n", "**Output:**\n", "\n", "```\n", "Enter a number: 2\n", "The result is 5.0.\n", "Execution finished.\n", "```\n", "\n", "```\n", "Enter a number: 0\n", "Error: division by zero.\n", "Execution finished.\n", "```\n", "\n", "```\n", "Enter a number: xyz\n", "Error: invalid input.\n", "Execution finished.\n", "```\n", "\n", "\n", "### Using `try/except` in functions\n", "\n", "It is common to use `try/except` inside functions to make them safer and more reusable.\n", "\n", "```python\n", "def divide(a, b):\n", " try:\n", " return a / b\n", " except ZeroDivisionError:\n", " return \"Error: division by zero.\"\n", "\n", "print(divide(10, 2)) # Output: 5.0\n", "print(divide(10, 0)) # Output: Error: division by zero.\n", "```\n", "\n", "**Output:**\n", "\n", "```\n", "5.0\n", "Error: division by zero.\n", "```\n", "\n", "\n", "### Summary Table\n", "\n", "| Block | When it is executed |\n", "| --------- | ------------------------------------------- |\n", "| `try` | Tries to execute code that may error |\n", "| `except` | Executes if an error occurs in the `try` |\n", "| `else` | Executes if **no error** occurs in the `try` |\n", "| `finally` | Always executes, with or without error |\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## File Handling\n", "\n", "So far, our programs' data lived only in memory: as soon as the program ended, everything was lost. To **store** information permanently (a report, a to-do list, settings), we write the data to **files**.\n", "\n", "### Opening a file: the `open()` function\n", "\n", "To work with a file, we first open it with `open()`, giving the **path** and the **mode**:\n", "\n", "```python\n", "file = open(\"data.txt\", \"r\", encoding=\"utf-8\")\n", "```\n", "\n", "
this example opens a file on your computer in read mode
\n", "\n", "The **mode** defines what we can do with the file:\n", "\n", "| Mode | Meaning | If the file does not exist |\n", "| --- | --- | --- |\n", "| `\"r\"` | read | raises an error |\n", "| `\"w\"` | write, erases previous content | creates a new one |\n", "| `\"a\"` | append, writes at the end | creates a new one |\n", "| `\"x\"` | exclusive creation | creates; error if it already exists |\n", "| `\"b\"` | binary mode (combine with others, e.g. `\"rb\"`) | not applicable |\n", "\n", "The `encoding=\"utf-8\"` parameter ensures that accented and special characters are read and written correctly.\n", "\n", "### The right way: the `with` block\n", "\n", "Every open file must be closed to release system resources. Instead of calling `file.close()` by hand (and risking forgetting it, or the program failing before that), we use the `with` block, which **closes the file automatically** at the end, even if an error occurs:\n", "\n", "```python\n", "with open(\"zen.txt\", \"w\", encoding=\"utf-8\") as file:\n", " file.write(\"Beautiful is better than ugly.\\n\")\n", " file.write(\"Explicit is better than implicit.\\n\")\n", " file.write(\"Simple is better than complex.\\n\")\n", "# outside the with, the file is already closed\n", "```\n", "\n", "
this example writes a file to your computer
\n", "\n", "```{admonition} Best practice - always use with\n", ":class: tip\n", "Always prefer `with open(...) as ...:`. It handles closing the file for you, avoiding resource leaks and corrupted files. It plays the same role as the `finally` block we saw in `try/except`.\n", "```\n", "\n", "### Reading a file\n", "\n", "There are a few ways to read the content of a file opened in read mode. The most direct reads everything at once:\n", "\n", "```python\n", "with open(\"zen.txt\", \"r\", encoding=\"utf-8\") as file:\n", " content = file.read() # reads the whole file as a single string\n", " print(content)\n", "```\n", "\n", "
this example reads a file from your computer all at once
\n", "\n", "For large files, reading **line by line** is more efficient. The file itself is iterable:\n", "\n", "```python\n", "with open(\"zen.txt\", \"r\", encoding=\"utf-8\") as file:\n", " for line in file:\n", " print(line.strip()) # strip() removes the trailing \"\\n\" of each line\n", "```\n", "\n", "
this example reads a file from your computer line by line
\n", "\n", "**Output:**\n", "\n", "```text\n", "Beautiful is better than ugly.\n", "Explicit is better than implicit.\n", "Simple is better than complex.\n", "```\n", "\n", "Other useful methods are `readline()` (reads one line at a time) and `readlines()` (returns a list with all the lines).\n", "\n", "```{admonition} Warning - write mode erases the content\n", ":class: warning\n", "Opening an existing file in `\"w\"` mode **erases all previous content** before writing. To add information without losing what is already there, use `\"a\"` (append) mode.\n", "```\n", "\n", "### Combining with error handling\n", "\n", "File operations fail for many reasons (the file does not exist, lack of permission). That is why files are often combined with `try/except`:\n", "\n", "```python\n", "try:\n", " with open(\"missing.txt\", \"r\", encoding=\"utf-8\") as file:\n", " print(file.read())\n", "except FileNotFoundError:\n", " print(\"Error: the file was not found.\")\n", "```\n", "\n", "
this example tries to read a file from your computer inside a try/except
\n", "\n", "**Output:**\n", "\n", "```text\n", "Error: the file was not found.\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Real-world data: dates, JSON and CSV\n", "\n", "Real projects constantly deal with **dates**, with **structured data** (JSON), and with **tables and spreadsheets** (CSV). Python's standard library already ships ready-made modules for all three.\n", "\n", "### Dates and times with `datetime`\n", "\n", "The `datetime` module represents dates and times and lets us do arithmetic with them:\n", "\n", "```python\n", "from datetime import datetime, timedelta\n", "\n", "now = datetime.now()\n", "print(now) # e.g. 2024-05-04 14:30:00.123456\n", "\n", "# Creating a specific date (year, month, day, hour, minute)\n", "premiere = datetime(1975, 4, 3, 20, 0)\n", "print(premiere.year, premiere.month) # 1975 4\n", "```\n", "\n", "To **display** a date in a readable format we use `strftime()`; to **parse** a date written as text we use `strptime()`:\n", "\n", "```python\n", "from datetime import datetime\n", "\n", "now = datetime.now()\n", "print(now.strftime(\"%d/%m/%Y at %H:%M\")) # e.g. 04/05/2024 at 14:30\n", "\n", "text = \"03/04/1975\"\n", "date = datetime.strptime(text, \"%d/%m/%Y\")\n", "print(date.year) # 1975\n", "```\n", "\n", "The difference between two dates becomes a `timedelta`, which we use for time arithmetic:\n", "\n", "```python\n", "from datetime import datetime, timedelta\n", "\n", "tv_premiere = datetime(1969, 10, 5) # first Monty Python episode\n", "today = datetime.now()\n", "days_elapsed = (today - tv_premiere).days\n", "print(f\"{days_elapsed} days have passed.\")\n", "\n", "in_one_week = today + timedelta(days=7)\n", "print(in_one_week)\n", "```\n", "\n", "```{admonition} Tip - the main format codes\n", ":class: tip\n", "The most common `strftime` and `strptime` codes are `%d` (day), `%m` (month), `%Y` (4-digit year), `%H` (hour), `%M` (minute) and `%S` (second).\n", "```\n", "\n", "### JSON: saving structured data\n", "\n", "**JSON** is the most widely used format for exchanging data between programs and on the web. The `json` module converts between Python dictionaries and lists and JSON text:\n", "\n", "```python\n", "import json\n", "\n", "character = {\n", " \"name\": \"King Arthur\",\n", " \"movie\": \"Monty Python and the Holy Grail\",\n", " \"has_horse\": False,\n", " \"knights\": [\"Lancelot\", \"Galahad\", \"Robin\"],\n", "}\n", "\n", "# dict -> JSON string\n", "json_text = json.dumps(character, indent=2, ensure_ascii=False)\n", "print(json_text)\n", "\n", "# JSON string -> dict\n", "back = json.loads(json_text)\n", "print(back[\"name\"]) # King Arthur\n", "```\n", "\n", "To read and write **directly to files**, use `json.dump()` and `json.load()`:\n", "\n", "```python\n", "# Write the dictionary to a .json file\n", "with open(\"character.json\", \"w\", encoding=\"utf-8\") as f:\n", " json.dump(character, f, indent=2, ensure_ascii=False)\n", "\n", "# Read the file back into a dictionary\n", "with open(\"character.json\", \"r\", encoding=\"utf-8\") as f:\n", " data = json.load(f)\n", "```\n", "\n", "
this example writes and reads a JSON file on your computer
\n", "\n", "```{admonition} Tip - readable JSON with accents\n", ":class: tip\n", "Use `indent=2` to produce indented JSON (easier to read) and `ensure_ascii=False` to keep accents and special characters instead of codes like `รก`.\n", "```\n", "\n", "Notice the pattern: `dumps` and `loads` (with an \"s\" for *string*) work with **text**, while `dump` and `load` work directly with **files**.\n", "\n", "### CSV: tables and spreadsheets\n", "\n", "**CSV** (*Comma-Separated Values*) stores data as a table, with one row per record and values separated by commas. It is the format spreadsheets like Excel export. The `csv` module reads and writes these files:\n", "\n", "```python\n", "import csv\n", "\n", "movies = [\n", " [\"title\", \"year\"],\n", " [\"Monty Python and the Holy Grail\", 1975],\n", " [\"Life of Brian\", 1979],\n", " [\"The Meaning of Life\", 1983],\n", "]\n", "\n", "# Writing (newline=\"\" avoids extra blank lines)\n", "with open(\"movies.csv\", \"w\", newline=\"\", encoding=\"utf-8\") as f:\n", " writer = csv.writer(f)\n", " writer.writerows(movies)\n", "\n", "# Reading back\n", "with open(\"movies.csv\", \"r\", encoding=\"utf-8\") as f:\n", " reader = csv.reader(f)\n", " for row in reader:\n", " print(row)\n", "```\n", "\n", "
this example writes and reads a CSV file on your computer
\n", "\n", "**Output:**\n", "\n", "```text\n", "['title', 'year']\n", "['Monty Python and the Holy Grail', '1975']\n", "['Life of Brian', '1979']\n", "['The Meaning of Life', '1983']\n", "```\n", "\n", "Note that CSV does not store the data type: the year `1975` comes back from reading as the string `'1975'`. When each column has a name, `csv.DictReader` and `csv.DictWriter` make the code clearer, reading each row as a dictionary.\n", "\n", "```{admonition} Going further - the pandas library\n", ":class: seealso\n", "For more serious table analysis (filtering, statistics, plots), the **pandas** library is the standard in the field. The standard-library `csv` module is enough for simple tasks and requires no installation.\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Modules, packages and virtual environments\n", "\n", "As programs grow, we stop writing everything in a single file. We organize the code into **modules** and **packages**, and we reuse libraries written by other people.\n", "\n", "### Modules: reusing code\n", "\n", "A **module** is simply a `.py` file. We have already used several standard-library modules, such as `random` and `datetime`. To use a module, just import it:\n", "\n", "```python\n", "import math\n", "\n", "print(math.sqrt(16)) # 4.0\n", "print(math.pi) # 3.141592653589793\n", "```\n", "\n", "We can import only what we need, or give it an alias with `as`:\n", "\n", "```python\n", "from math import sqrt, pi # imports specific names\n", "import numpy as np # alias, common with libraries\n", "```\n", "\n", "
\n", "\n", "### Creating your own module\n", "\n", "Any `.py` file of yours is also a module. Suppose a file `greetings.py`:\n", "\n", "```python\n", "# greetings.py\n", "def hello(name):\n", " return f\"Hello, {name}!\"\n", "\n", "PI = 3.14159\n", "```\n", "\n", "
\n", "\n", "In another file, in the same folder, you import it by name (without the `.py`):\n", "\n", "```python\n", "import greetings\n", "\n", "print(greetings.hello(\"Arthur\")) # Hello, Arthur!\n", "```\n", "\n", "
this example imports a module that you create yourself
\n", "\n", "### The `if __name__ == \"__main__\"` idiom\n", "\n", "When a file can be both **run directly** and **imported** by another, we protect the test code with `if __name__ == \"__main__\":`. This block only runs when the file is executed directly, not when it is imported:\n", "\n", "```python\n", "# greetings.py\n", "def hello(name):\n", " return f\"Hello, {name}!\"\n", "\n", "if __name__ == \"__main__\":\n", " # Only runs when executing: python greetings.py\n", " print(hello(\"world\"))\n", "```\n", "\n", "### Packages\n", "\n", "A **package** is a folder that groups several related modules. In practice, it is a directory with `.py` files (and, traditionally, an `__init__.py` file). Large libraries such as NumPy and Matplotlib are packages.\n", "\n", "### Installing libraries with pip\n", "\n", "Not everything ships in the standard library. Thousands of third-party libraries are available on **PyPI** (*Python Package Index*) and are installed with **pip**, Python's package manager. In the terminal:\n", "\n", "```bash\n", "pip install requests\n", "```\n", "\n", "Once installed, the library is used like any other:\n", "\n", "```python\n", "import requests\n", "\n", "response = requests.get(\"https://api.github.com\")\n", "print(response.status_code) # 200\n", "```\n", "\n", "```{admonition} Tip - pip in Colab and Jupyter\n", ":class: tip\n", "Inside a notebook (such as Google Colab), run pip in a cell prefixed with `!`, like this: `!pip install requests`. The `!` marks the line as a terminal command, not Python code.\n", "```\n", "\n", "### Virtual environments (`venv`)\n", "\n", "Different projects may need different versions of the same library. To avoid conflicts, we create a **virtual environment**: an isolated installation of Python and its libraries, separate per project. The `venv` module does this. In the terminal:\n", "\n", "```bash\n", "# Creates the environment in a folder named .venv\n", "python -m venv .venv\n", "\n", "# Activates the environment\n", "source .venv/bin/activate # Linux and macOS\n", ".venv\\Scripts\\activate # Windows\n", "\n", "# With the environment active, pip installs only into it\n", "pip install requests\n", "\n", "# When done, deactivate\n", "deactivate\n", "```\n", "\n", "```{admonition} Best practice - one environment per project\n", ":class: tip\n", "Create a virtual environment for each project and avoid installing everything into the system Python. This way, the dependencies of one project do not interfere with another's, and it is easy to recreate the environment on another machine (for example, by listing the libraries in a `requirements.txt` file).\n", "```\n", "\n", "### A modern alternative: `uv`\n", "\n", "In recent years, **uv** has become popular as a fast replacement for `pip` and `venv`. Written in Rust by Astral, it is much faster and combines, in a single command, creating environments and installing packages.\n", "\n", "Installation (once, on the system):\n", "\n", "```bash\n", "# Official installer (Linux and macOS)\n", "curl -LsSf https://astral.sh/uv/install.sh | sh\n", "\n", "# Or, if you already have pip\n", "pip install uv\n", "```\n", "\n", "In daily use, it creates the environment and installs packages much faster than `pip`:\n", "\n", "```bash\n", "# Creates the virtual environment (equivalent to python -m venv .venv)\n", "uv venv\n", "\n", "# Installs packages (pip-compatible interface)\n", "uv pip install requests\n", "```\n", "\n", "uv also manages the whole project, tracking dependencies in a `pyproject.toml` file:\n", "\n", "```bash\n", "uv init my_project # creates the project structure\n", "cd my_project\n", "uv add requests # adds a dependency (and updates pyproject.toml)\n", "uv run main.py # runs the script inside the project environment\n", "```\n", "\n", "```{admonition} Tip - why uv?\n", ":class: tip\n", "`uv` plays the role of `venv` and `pip` (and more) while being dozens of times faster. The `uv venv` and `uv pip install` commands are almost identical to the ones you already know, so the transition is easy. It is today the recommended tool for new projects.\n", "```\n", "\n", "```{admonition} Learn more\n", ":class: seealso\n", "Official pip and PyPI documentation: . The full list of standard-library modules (which need no installation) is at . uv documentation: .\n", "```" ] } ], "metadata": { "colab": { "provenance": [], "toc_visible": true }, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "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.9.20" } }, "nbformat": 4, "nbformat_minor": 4 }