diff --git a/04_input_output.ipynb b/04_input_output.ipynb index 7a4c3153..0666a1d6 100644 --- a/04_input_output.ipynb +++ b/04_input_output.ipynb @@ -40,11 +40,11 @@ " - [Reading/Writing CSV files](#Reading/Writing-CSV-files)\n", " - [Quiz on CSV](#Quiz-on-CSV)\n", " - [Exercises](#Exercises)\n", - " - [Exercise 1: CSV to dictionary 🌶️](#Exercise-1:-CSV-to-dictionary-🌶️)\n", - " - [Exercise 2: Counting words 🌶️](#Exercise-2:-Counting-words-🌶️)\n", - " - [Exercise 3: Letter statistics 🌶️🌶️](#Exercise-3:-Letter-statistics-🌶️🌶️)\n", - " - [Exercise 4: Translating words 🌶️🌶️](#Exercise-4:-Translating-words-🌶️🌶️)\n", - " - [Exercise 5: Binary format 🌶️🌶️🌶️](#Exercise-5:-Binary-format-🌶️🌶️🌶️)" + " - [Exercise 1: CSV to dictionary](#Exercise-1:-CSV-to-dictionary)\n", + " - [Exercise 2: Counting words](#Exercise-2:-Counting-words)\n", + " - [Exercise 3: Letter statistics](#Exercise-3:-Letter-statistics)\n", + " - [Exercise 4: Translating words](#Exercise-4:-Translating-words)\n", + " - [Exercise 5: Binary format](#Exercise-5:-Binary-format)" ] }, { @@ -75,13 +75,7 @@ "- connecting to databases or other network services\n", "\n", "\n", - "The majority of these operations are covered by the Python standard library. We are going to see how to use them in this chapter.\n", - "\n", - "
\n", - "

Note

\n", - "In reference to the chapter on functional programming, it is interesting to note that these functions perform side-effects. Therefore, any code containing these operations is no longer pure and is not referentially transparent. The same function can return different values for the same argument if called multiple times, and the function can have long-distance effects. That means they can modify the program state elsewhere, leading to unexpected results.

\n", - "Therefore, we suggest separating input and output from the other computations in your program. For example, if you have a complex calculation requiring several user inputs at several stages of the process, consider writing a function that only performs the calculation given all inputs and then requires all inputs separately, for example, through a single file. This makes your code easier to debug, test and understand.\n", - "
" + "The majority of these operations are covered by the Python standard library. We are going to see how to use them in this chapter." ] }, { @@ -108,8 +102,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "It is also possible to print any other python object using `print`. \n", - "In that case, the `__str__` **magic method** on that object's class is [called](https://docs.python.org/3/reference/datamodel.html#object.__str__)." + "It is also possible to print any other python object using `print`." ] }, { @@ -245,7 +238,7 @@ "metadata": {}, "outputs": [], "source": [ - "%%ipytest debug\n", + "%%ipytest\n", "\n", "def solution_print_odd(n: int) -> None: \n", " \"\"\"Prints all odd numbers from 1 to n\n", @@ -255,8 +248,7 @@ "\n", " Returns:\n", " - None (prints to console)\n", - " \"\"\"\n", - " pass" + " \"\"\"" ] }, { @@ -287,7 +279,6 @@ " Returns:\n", " - None (prints to console)\n", " \"\"\"\n", - " pass\n", "\n", "solution_print_salutation()" ] @@ -526,7 +517,7 @@ "metadata": {}, "source": [ "\n", - "1. Modify the function `solution_find_all_files` to find all files and directories in the [data](./tutorial/tests/data/) (./tutorial/tests/data/) directory and return them as a list of `Path` objects\n", + "1. Modify the function `solution_find_all_files` to find all files and directories in the ```/tutorial/tests/data``` directory and return them as a list of `Path` objects\n", "\n", "
\n", " Hint: The path to the data directory is available as the argument current_path of the function solution_find_all_files\n", @@ -613,8 +604,7 @@ "\n", " Returns:\n", " - The number of directories in the directory\n", - " \"\"\"\n", - " pass\n" + " \"\"\"" ] }, { @@ -627,9 +617,9 @@ "### Reading from a file\n", "\n", "We now want to learn how to read text from a file. \n", - "Let's see how to do this with an example: we want to open the file [hello.txt](./data/hello.txt) and read its contents.\n", + "Let's see how to do this with an example: we want to open the file [hello.txt](./tutorial/tests/data/hello.txt) and read its contents.\n", "\n", - "1. The path is already identified, we know the file is in `./data/hello.txt`. We save this in a variable `path`.\n", + "1. The path is already identified, we know the file is in [hello.txt](./tutorial/tests/data/hello.txt). We save this in a variable `path`.\n", "2. We now can use the built-in function [`open`](https://docs.python.org/3/library/functions.html#open) to open the file. This function returns a [file object](https://docs.python.org/3/glossary.html#term-file-object) that we can use to further manipulate the file. To ensure we only open the file for reading, we pass the string \"r\" to the second argument of `open`.\n", "3. Now we can read the contents using `read`, `readline` or `readlines`. `read` reads the whole file content into a single string, `readline` reads one line, while `readlines` reads the whole file content as a list of strings, one item per line in the file. This knowledge is useful when we only want to read part of a file or when the file is too big to fit in memory and we can only read parts.\n", "4. Finally, we close the file using the `close` method on the file object.\n" @@ -773,7 +763,7 @@ " Returns:\n", " - A list of strings, each representing a line in the file\n", " \"\"\"\n", - " pass" + " return" ] }, { @@ -787,7 +777,7 @@ "- We use `write` to write a *string* to the file. Other types of object should be converted to string before being written.\n", "\n", "\n", - "Let's see this in action by writing your name in a file called `me.txt` in [data](./data/)" + "Let's see this in action by writing your name in a file called `me.txt` in ```/tutorial/tests/data```" ] }, { @@ -902,8 +892,7 @@ "\n", " Returns:\n", " - None (writes to file)\n", - " \"\"\"\n", - " pass" + " \"\"\"" ] }, { @@ -911,17 +900,23 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "2. Modify the function `solution_read_write_file` to read the lines from the file `input_file` and write them in the form `line, length`, to the file `output_file`. Here `line` is the line of text in `input_file` **without the line ending**, `length` is **number of characters** in that line **without the line separator**.\n", - "If `input_file` contains these lines:\n", + "2. Modify the function `solution_read_write_file` to read the lines from the file `input_file` and write them in the form `line, length`, to the file `output_file`. Here `line` is the line of text in `input_file` **without the line ending (\\r\\n)**, `length` is **number of characters** in that line. The characters are added automatically by the operating system when reading a file. Windows uses **\\r\\n** whereas Linux and MacOS use only **\\n**. To remove the line ending characters, use ```strip(\"\\r\\n\")```, e.g.: ```line.strip(\"\\r\\n\")```.\n", + "\n", + " If `input_file` contains these lines:\n", + " \n", " ```\n", " first\n", " second\n", " ```\n", + " \n", " we expect the output file to contain these lines:\n", + " \n", " ```\n", " first, 5\n", " second, 6\n", - " ```" + " ```\n", + " \n", + " Do not forget to add a **\\n** when writing lines in the file." ] }, { @@ -945,8 +940,7 @@ "\n", " Returns:\n", " - None (writes to file)\n", - " \"\"\"\n", - " pass" + " \"\"\"" ] }, { @@ -1201,7 +1195,9 @@ "source": [ "message = \"Ciao\"\n", "message_secret = bytes(message, \"utf-8\")\n", - "[print(f\"The `uft8` codepoint is = {enc}, the bytes representation = {enc.to_bytes(4, 'little')}, the representation is {chr(enc)}\") for plain, enc in zip(message, message_secret)]" + "print_messages = [f\"The `uft8` codepoint is = {enc}, the bytes representation = {enc.to_bytes(4, 'little')}, the representation is {chr(enc)}\" for plain, enc in zip(message, message_secret)]\n", + "for msg in print_messages:\n", + " print(msg)" ] }, { @@ -1225,7 +1221,7 @@ "These packages are outside of the scope of this tutorial and will not be covered here.\n", "\n", "\n", - "Let's see how to read csv files using `csv` with an example by reading [example.csv](./data/example.csv):" + "Let's see how to read csv files using `csv` with an example by reading [example.csv](./tutorial/tests/data/example.csv):" ] }, { @@ -1330,7 +1326,7 @@ "tags": [] }, "source": [ - "### Exercise 1: CSV to dictionary 🌶️" + "### Exercise 1: CSV to dictionary" ] }, { @@ -1340,6 +1336,8 @@ "tags": [] }, "source": [ + "**Difficulty: 🌶️**\n", + "\n", "Write a function that reads a CSV file and returns a dictionary.\n", "\n", "- The dictionary keys are in the first **column**\n", @@ -1404,7 +1402,7 @@ " Returns:\n", " - A dictionary with each row represented as a key, value pair\n", " \"\"\"\n", - " pass" + " return" ] }, { @@ -1414,7 +1412,7 @@ "tags": [] }, "source": [ - "### Exercise 2: Counting words 🌶️" + "### Exercise 2: Counting words" ] }, { @@ -1424,6 +1422,8 @@ "tags": [] }, "source": [ + "**Difficulty: 🌶️**\n", + "\n", "Write a function to read all the lines from `input_file` and count the number of words in the file. The solution should be a single number.\n", "\n", "For example, for the file\n", @@ -1443,7 +1443,7 @@ " The file is available as the parameter input_file of solution_exercise2 function\n", " \n", "
  • \n", - " A word consists of printable characters without whitespaces, line breaks etc. Have a look at the basic_datatypes notebook if you forgot how to split a text into it's words.\n", + " A word consists of printable characters without whitespaces, line breaks etc. Have a look at the basic_datatypes notebook if you forgot how to split a text into its words.\n", "
  • \n", " \n", "
    \n", @@ -1472,7 +1472,7 @@ " Returns:\n", " - The number of words in the file\n", " \"\"\"\n", - " pass" + " return" ] }, { @@ -1482,7 +1482,7 @@ "tags": [] }, "source": [ - "### Exercise 3: Letter statistics 🌶️🌶️" + "### Exercise 3: Letter statistics" ] }, { @@ -1492,7 +1492,9 @@ "tags": [] }, "source": [ - "Write a function that reads all the lines from `lines.txt` and counts the occurences of every letter present in all the words.\n", + "**Difficulty: 🌶️🌶️**\n", + "\n", + "Write a function that reads all the lines from [lines.txt](./tutorial/tests/data/lines.txt) and counts the occurences of every letter present in all the words.\n", "\n", "The result should be a dictionary, where each key-value pair is like `{letter: count}`. For example, `{a: 5}` means that the letter `a` appeared five times in this file.\n", "\n", @@ -1522,7 +1524,7 @@ "metadata": {}, "outputs": [], "source": [ - "%%ipytest input_output\n", + "%%ipytest\n", "\n", "import pathlib as pl\n", "import string\n", @@ -1538,7 +1540,7 @@ " Returns:\n", " - A dictionary with a count of each letter\n", " \"\"\"\n", - " pass" + " return" ] }, { @@ -1548,7 +1550,7 @@ "tags": [] }, "source": [ - "### Exercise 4: Translating words 🌶️🌶️" + "### Exercise 4: Translating words" ] }, { @@ -1558,7 +1560,9 @@ "tags": [] }, "source": [ - "Write a function which takes the words from the file `english.txt` and translates them to Italian using the dictionary file `dict.csv`. The output should be a **list of tuples** with the pair `italian, english` if the word is found and nothing otherwise.\n", + "**Difficulty: 🌶️🌶️**\n", + "\n", + "Write a function which takes the words from the file [english.txt](./tutorial/tests/data/english.txt) and translates them to Italian using the dictionary file [dict.csv](./tutorial/tests/data/dict.csv). The output should be a **list of tuples** with the pair `italian, english` **if the word is found and nothing otherwise**.\n", "\n", "For example, given the `english.txt` file:\n", "\n", @@ -1600,6 +1604,7 @@ "source": [ "%%ipytest\n", "\n", + "import csv\n", "import pathlib as pl\n", "\n", "def solution_exercise4(english: pl.Path, dictionary: pl.Path) -> list[(str, str)]:\n", @@ -1615,8 +1620,7 @@ " Returns:\n", " - A list of tuples with the english / italian words\n", " \"\"\"\n", - "\n", - " pass" + " return" ] }, { @@ -1626,7 +1630,7 @@ "tags": [] }, "source": [ - "### Exercise 5: Binary format 🌶️🌶️🌶️" + "### Exercise 5: Binary format" ] }, { @@ -1636,8 +1640,9 @@ "tags": [] }, "source": [ + "**Difficulty: 🌶️🌶️🌶️**\n", "\n", - "The file `super_secret.dat` contains a secret message. We know that the message is stored in binary format as a sequence of bytes. The message starts with the byte sequence `b'\\xff\\xee\\xdd\\xcc\\xbb\\xaa'` and finishes with `b'\\xaa\\xbb\\xcc\\xdd\\xee\\xff'`. \n", + "The file `secret_file` contains a secret message. We know that the message is stored in binary format as a sequence of bytes. The message starts with the byte sequence `b'\\xff\\xee\\xdd\\xcc\\xbb\\xaa'` and finishes with `b'\\xaa\\xbb\\xcc\\xdd\\xee\\xff'`. \n", "Write a function that reads the file and returns **only** the secret message as a string.\n", "\n", "\n", @@ -1678,14 +1683,14 @@ " Returns:\n", " - The secret message\n", " \"\"\"\n", - " pass" + " return" ] } ], "metadata": { "celltoolbar": "Slideshow", "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -1699,7 +1704,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.4" + "version": "3.13.5" } }, "nbformat": 4, diff --git a/tutorial/quiz/input_output.py b/tutorial/quiz/input_output.py index 923b907e..9bd6a539 100644 --- a/tutorial/quiz/input_output.py +++ b/tutorial/quiz/input_output.py @@ -43,7 +43,7 @@ def __init__(self, title=""): question="What happens if you call input() in the middle of a function?", options={ "The function execution stops and it waits for the user to type an input": "Correct! Input is a blocking function which waits for user to enter a string in the console and press enter.", - "The function continues its execution": "Wrong!", + "The function continues its execution": "Wrong! Input is a blocking function which waits for user input.", }, correct_answer="The function execution stops and it waits for the user to type an input", shuffle=True, @@ -57,7 +57,7 @@ def __init__(self, title=""): q1 = Question( question="What does the operator / do when applied to two Pathlib.Path objects?", options={ - "It removes the second path from the first": "Wrong, try it in the shell.", + "It removes the second path from the first": "Wrong, try it in a cell below.", "It concatenates paths": "Correct, it lets you construct a path from different segments", }, correct_answer="It concatenates paths", @@ -68,7 +68,7 @@ def __init__(self, title=""): question="If you use Pathlib, do you need to use different path separators on Windows and Linux to combine path segments?", options={ "No, you can combine Pathlib.Path objects with /": "Correct! Pathlib will then generate the correct path for your OS.", - "Yes": "Wrong! You can always use /", + "Yes": "Wrong! You can always use /", }, correct_answer="No, you can combine Pathlib.Path objects with /", shuffle=True, @@ -78,7 +78,7 @@ def __init__(self, title=""): question="""The path Pathlib.Path("./") represent a relative path. What location does it refer to?""", options={ "Relative to the current working directory, the location of the current Python script being run": "Correct!", - "Relative to the user's home directory": "Wrong!", + "Relative to the user's home directory": "Wrong! It is relative to the current working directory.", }, correct_answer="Relative to the current working directory, the location of the current Python script being run", shuffle=True, diff --git a/tutorial/tests/data/example1.csv b/tutorial/tests/data/example1.csv new file mode 100644 index 00000000..ab2340cd --- /dev/null +++ b/tutorial/tests/data/example1.csv @@ -0,0 +1,24 @@ +<<<<<<< HEAD +this,is,data +0,1,2 +1,2,3 +2,3,4 +3,4,5 +4,5,6 +5,6,7 +6,7,8 +7,8,9 +8,9,10 +======= +this,is,data +0,1,2 +1,2,3 +2,3,4 +3,4,5 +4,5,6 +5,6,7 +6,7,8 +7,8,9 +8,9,10 +>>>>>>> 5bd589eb6742c15e877451b291ee8d04a405d4b1 +9,10,11 diff --git a/tutorial/tests/data/me.txt b/tutorial/tests/data/me.txt new file mode 100644 index 00000000..e09cfaf8 --- /dev/null +++ b/tutorial/tests/data/me.txt @@ -0,0 +1 @@ +Simone diff --git a/tutorial/tests/data/numbers.txt b/tutorial/tests/data/numbers.txt new file mode 100644 index 00000000..8b1acc12 --- /dev/null +++ b/tutorial/tests/data/numbers.txt @@ -0,0 +1,10 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 diff --git a/tutorial/tests/data/output.txt b/tutorial/tests/data/output.txt new file mode 100644 index 00000000..6a1b6037 Binary files /dev/null and b/tutorial/tests/data/output.txt differ diff --git a/tutorial/tests/data/output1.txt b/tutorial/tests/data/output1.txt new file mode 100644 index 00000000..f7dff7ae Binary files /dev/null and b/tutorial/tests/data/output1.txt differ diff --git a/tutorial/tests/test_04_input_output.py b/tutorial/tests/test_04_input_output.py index b7b9fa69..13fb902c 100644 --- a/tutorial/tests/test_04_input_output.py +++ b/tutorial/tests/test_04_input_output.py @@ -254,10 +254,10 @@ def reference_exercise5(secret_file: pl.Path) -> str: def test_exercise5(function_to_test): message = get_data("secret_message.dat") # Save the original content of the input file - original_content = message.read_text() + original_content = message.read_bytes() try: assert function_to_test(message) == reference_exercise5(message) finally: # Reset the input file to its original content - message.write_text(original_content) + message.write_bytes(original_content)