Repository: jerry-git/learn-python3 Branch: master Commit: 85e30d370ec2 Files: 85 Total size: 14.7 MB Directory structure: gitextract_x63mlfur/ ├── .gitattributes ├── .github/ │ └── workflows/ │ └── ci.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── _config.yml ├── dev-requirements.txt ├── notebooks/ │ ├── beginner/ │ │ ├── data/ │ │ │ ├── empty_file.txt │ │ │ ├── numbers.txt │ │ │ ├── random_data.txt │ │ │ ├── simple_file.txt │ │ │ └── simple_file_with_empty_lines.txt │ │ ├── exercises/ │ │ │ ├── 01_strings_exercise.ipynb │ │ │ ├── 02_numbers_exercise.ipynb │ │ │ ├── 03_conditionals_exercise.ipynb │ │ │ ├── 04_lists_exercise.ipynb │ │ │ ├── 05_dictionaries_exercise.ipynb │ │ │ ├── 06_for_loops_exercise.ipynb │ │ │ ├── 07_functions_exercise.ipynb │ │ │ ├── 08_testing1_exercise.ipynb │ │ │ ├── 09_recap1_exercise.ipynb │ │ │ ├── 10_file_io_exercise.ipynb │ │ │ ├── 11_classes_exercise.ipynb │ │ │ ├── 12_exceptions_exercise.ipynb │ │ │ ├── 14_debugging_exercise.ipynb │ │ │ ├── 15_std_lib1_exercise.ipynb │ │ │ ├── 16_testing2_exercise.ipynb │ │ │ └── 19_recap2_exercise.ipynb │ │ ├── html/ │ │ │ ├── 01_strings.html │ │ │ ├── 02_numbers.html │ │ │ ├── 03_conditionals.html │ │ │ ├── 04_lists.html │ │ │ ├── 05_dictionaries.html │ │ │ ├── 06_for_loops.html │ │ │ ├── 07_functions.html │ │ │ ├── 08_testing1.html │ │ │ ├── 10_file_io.html │ │ │ ├── 11_classes.html │ │ │ ├── 12_exceptions.html │ │ │ ├── 13_modules_and_packages.html │ │ │ ├── 14_debugging.html │ │ │ ├── 15_std_lib.html │ │ │ ├── 16_testing2.html │ │ │ ├── 17_venv.html │ │ │ └── 18_project_structure.html │ │ └── notebooks/ │ │ ├── 01_strings.ipynb │ │ ├── 02_numbers.ipynb │ │ ├── 03_conditionals.ipynb │ │ ├── 04_lists.ipynb │ │ ├── 05_dictionaries.ipynb │ │ ├── 06_for_loops.ipynb │ │ ├── 07_functions.ipynb │ │ ├── 08_testing1.ipynb │ │ ├── 10_file_io.ipynb │ │ ├── 11_classes.ipynb │ │ ├── 12_exceptions.ipynb │ │ ├── 13_modules_and_packages.ipynb │ │ ├── 14_debugging.ipynb │ │ ├── 15_std_lib.ipynb │ │ ├── 16_testing2.ipynb │ │ ├── 17_venv.ipynb │ │ └── 18_project_structure.ipynb │ └── intermediate/ │ ├── data/ │ │ ├── empty.txt │ │ ├── misc_data1.txt │ │ └── misc_data2.txt │ ├── exercises/ │ │ ├── 01_std_lib2_exercise.ipynb │ │ └── 05_idiomatic_python_exercise.ipynb │ ├── html/ │ │ ├── 01_best_practices.html │ │ ├── 01_idiomatic_loops.html │ │ ├── 01_pytest_fixtures.html │ │ ├── 01_std_lib2.html │ │ ├── 02_idiomatic_dicts.html │ │ ├── 03_idiomatic_misc1.html │ │ └── 04_idiomatic_misc2.html │ └── notebooks/ │ ├── 01_best_practices.ipynb │ ├── 01_idiomatic_loops.ipynb │ ├── 01_pytest_fixtures.ipynb │ ├── 01_std_lib2.ipynb │ ├── 02_idiomatic_dicts.ipynb │ ├── 03_idiomatic_misc1.ipynb │ └── 04_idiomatic_misc2.ipynb ├── pytest.ini └── scripts/ └── notebook_to_html.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ notebooks/* linguist-language=Python ================================================ FILE: .github/workflows/ci.yml ================================================ name: CI on: pull_request: push: branches: - "**" jobs: checks: runs-on: ubuntu-latest strategy: fail-fast: false matrix: python-version: [ "3.10", "3.11" ] steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - run: pip install -r dev-requirements.txt - run: pre-commit run -a - run: pytest --nbval notebooks ================================================ FILE: .gitignore ================================================ .DS_Store *ipynb_checkpoints* .idea* .pytest_cache* .tox* *pycache* sync-*.sh tmp.txt my_log.txt ================================================ FILE: .pre-commit-config.yaml ================================================ repos: - repo: local hooks: - id: clear-nb-output name: clear-nb-output files: \.ipynb$ language: system entry: jupyter nbconvert --ClearOutputPreprocessor.enabled=True --inplace - id: black name: black entry: black language: system files: \.(py|ipynb)$ - id: pyupgrade name: pyupgrade entry: nbqa pyupgrade --py310-plus language: system files: \.(py|ipynb)$ ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing If you spot a typo or the content does not make sense in some way, feel free to open an issue or directly create a PR. I am open for enhancement ideas and feature requests. If there's some topic you'd like to see a notebook about, feel free to open an issue and request it there. ## Development Install development dependencies ``` pip install -r dev-requirements.txt ``` #### Generating html ``` python scripts/notebook_to_html.py ``` #### Testing ``` pytest --nbval notebooks ``` #### pre-commit ``` pre-commit install ``` and it'll automatically run all the pre-commit hooks for each commit. ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2023 jerry-git Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================

logo

# Learn Python 3 ## Introduction This repository contains a collection of materials for teaching/learning Python 3 (3.10+). #### Requirements * Have Python 3.10 or newer installed. You can check the version by typing `python3 --version` in your command line. You can download the latest Python version from [here](https://www.python.org/downloads/). * Have [Jupyter Notebook installed](http://jupyter.readthedocs.io/en/latest/install.html). `pip install jupyter` is sufficient in most cases. If you can not access Python and/or Jupyter Notebook on your machine, you can still follow the web based materials. However, you should be able to use Jupyter Notebook in order to complete the exercises. #### Usage (locally) 1. Clone or download this repository. 2. Run `jupyter notebook` command in your command line in the repository directory. 3. Jupyter Notebook session will open in the browser and you can start navigating through the materials. #### Usage (Binder in the cloud) You can also just use [Binder:](https://mybinder.org/) By clicking of this [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/jerry-git/learn-python3/master) badge, the project is opened in a Jupyter instance in the cloud and you can then navigate to the folders containing the notebooks and start them each and interactively explore them! #### Contributing See [contributing](https://github.com/jerry-git/learn-python3/blob/master/CONTRIBUTING.md) guide. ## Beginner 1. [Strings](https://jerry-git.github.io/learn-python3/notebooks/beginner/html/01_strings.html) [[notebook]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/notebooks/01_strings.ipynb) [[exercise]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/exercises/01_strings_exercise.ipynb) 2. [Numbers](https://jerry-git.github.io/learn-python3/notebooks/beginner/html/02_numbers.html) [[notebook]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/notebooks/02_numbers.ipynb) [[exercise]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/exercises/02_numbers_exercise.ipynb) 3. [Conditionals](https://jerry-git.github.io/learn-python3/notebooks/beginner/html/03_conditionals.html) [[notebook]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/notebooks/03_conditionals.ipynb) [[exercise]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/exercises/03_conditionals_exercise.ipynb) 4. [Lists](https://jerry-git.github.io/learn-python3/notebooks/beginner/html/04_lists.html) [[notebook]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/notebooks/04_lists.ipynb) [[exercise]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/exercises/04_lists_exercise.ipynb) 5. [Dictionaries](https://jerry-git.github.io/learn-python3/notebooks/beginner/html/05_dictionaries.html) [[notebook]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/notebooks/05_dictionaries.ipynb) [[exercise]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/exercises/05_dictionaries_exercise.ipynb) 6. [For loops](https://jerry-git.github.io/learn-python3/notebooks/beginner/html/06_for_loops.html) [[notebook]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/notebooks/06_for_loops.ipynb) [[exercise]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/exercises/06_for_loops_exercise.ipynb) 7. [Functions](https://jerry-git.github.io/learn-python3/notebooks/beginner/html/07_functions.html) [[notebook]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/notebooks/07_functions.ipynb) [[exercise]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/exercises/07_functions_exercise.ipynb) 8. [Testing with pytest - part 1](https://jerry-git.github.io/learn-python3/notebooks/beginner/html/08_testing1.html) [[notebook]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/notebooks/08_testing1.ipynb) [[exercise]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/exercises/08_testing1_exercise.ipynb) 9. Recap exercise 1 [[exercise]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/exercises/09_recap1_exercise.ipynb) 10. [File I\O](https://jerry-git.github.io/learn-python3/notebooks/beginner/html/10_file_io.html) [[notebook]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/notebooks/10_file_io.ipynb) [[exercise]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/exercises/10_file_io_exercise.ipynb) 11. [Classes](https://jerry-git.github.io/learn-python3/notebooks/beginner/html/11_classes.html) [[notebook]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/notebooks/11_classes.ipynb) [[exercise]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/exercises/11_classes_exercise.ipynb) 12. [Exceptions](https://jerry-git.github.io/learn-python3/notebooks/beginner/html/12_exceptions.html) [[notebook]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/notebooks/12_exceptions.ipynb) [[exercise]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/exercises/12_exceptions_exercise.ipynb) 13. [Modules and packages](https://jerry-git.github.io/learn-python3/notebooks/beginner/html/13_modules_and_packages.html) [[notebook]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/notebooks/13_modules_and_packages.ipynb) 14. [Debugging](https://jerry-git.github.io/learn-python3/notebooks/beginner/html/14_debugging.html) [[notebook]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/notebooks/14_debugging.ipynb) [[exercise]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/exercises/14_debugging_exercise.ipynb) 15. [Goodies of the Standard Library - part 1](https://jerry-git.github.io/learn-python3/notebooks/beginner/html/15_std_lib.html) [[notebook]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/notebooks/15_std_lib.ipynb) [[exercise]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/exercises/15_std_lib1_exercise.ipynb) 16. [Testing with pytest - part 2](https://jerry-git.github.io/learn-python3/notebooks/beginner/html/16_testing2.html) [[notebook]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/notebooks/16_testing2.ipynb) [[exercise]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/exercises/16_testing2_exercise.ipynb) 17. [Virtual environment](https://jerry-git.github.io/learn-python3/notebooks/beginner/html/17_venv.html) [[notebook]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/notebooks/17_venv.ipynb) 18. [Project structure](https://jerry-git.github.io/learn-python3/notebooks/beginner/html/18_project_structure.html) [[notebook]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/notebooks/18_project_structure.ipynb) 19. Recap exercise 2 [[exercise]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/beginner/exercises/19_recap2_exercise.ipynb) ## Intermediate #### Idiomatic Python Python is a powerful language which contains many features not presented in most other programming languages. Idiomatic section will cover some of these Pythonic features in detail. These materials are especially useful for people with background in other programming languages. 1. [Idiomatic loops](https://jerry-git.github.io/learn-python3/notebooks/intermediate/html/01_idiomatic_loops.html) [[notebook]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/intermediate/notebooks/01_idiomatic_loops.ipynb) 2. [Idiomatic dictionaries](https://jerry-git.github.io/learn-python3/notebooks/intermediate/html/02_idiomatic_dicts.html) [[notebook]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/intermediate/notebooks/02_idiomatic_dicts.ipynb) 3. [Idiomatic Python - miscellaneous part 1](https://jerry-git.github.io/learn-python3/notebooks/intermediate/html/03_idiomatic_misc1.html) [[notebook]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/intermediate/notebooks/03_idiomatic_misc1.ipynb) 4. [Idiomatic Python - miscellaneous part 2](https://jerry-git.github.io/learn-python3/notebooks/intermediate/html/04_idiomatic_misc2.html) [[notebook]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/intermediate/notebooks/04_idiomatic_misc2.ipynb) 5. Idiomatic Python exercise [[exercise]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/intermediate/exercises/05_idiomatic_python_exercise.ipynb) #### Step up your `pytest` game 1. [Efficient use of fixtures](https://jerry-git.github.io/learn-python3/notebooks/intermediate/html/01_pytest_fixtures.html) [[notebook]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/intermediate/notebooks/01_pytest_fixtures.ipynb) #### Best practices A list of best development practices for Python projects. Most of the practices listed here are also applicable for other languages, however the presented tooling focuses mainly on Python. 1. [Best practices](https://jerry-git.github.io/learn-python3/notebooks/intermediate/html/01_best_practices.html) [[notebook]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/intermediate/notebooks/01_best_practices.ipynb) #### General topics 1. [Goodies of the Standard Library - part 2](https://jerry-git.github.io/learn-python3/notebooks/intermediate/html/01_std_lib2.html) [[notebook]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/intermediate/notebooks/01_std_lib2.ipynb) [[exercise]](http://nbviewer.jupyter.org/github/jerry-git/learn-python3/blob/master/notebooks/intermediate/exercises/01_std_lib2_exercise.ipynb) ## Credits * Logo: Abdur-Rahmaan Janhangeer, [@Abdur-rahmaanJ](https://github.com/Abdur-rahmaanJ) ================================================ FILE: _config.yml ================================================ theme: jekyll-theme-slate ================================================ FILE: dev-requirements.txt ================================================ black[jupyter] jupyter nbqa[toolchain] nbval pre-commit pytest ================================================ FILE: notebooks/beginner/data/empty_file.txt ================================================ ================================================ FILE: notebooks/beginner/data/numbers.txt ================================================ 1 2.5 16 4 5 12 15 16 18 100 ================================================ FILE: notebooks/beginner/data/random_data.txt ================================================ 78, 25, 4, 87, 18, 77, 94, 37, 22, 11, 28, 43, 49, 79, 75, 74, 70, 20, 50, 46 ================================================ FILE: notebooks/beginner/data/simple_file.txt ================================================ First line Second line Third And so the story goes! ================================================ FILE: notebooks/beginner/data/simple_file_with_empty_lines.txt ================================================ The Title of the Story First paragraph with some nonsense words. Then we move to second paragraph and so on. ================================================ FILE: notebooks/beginner/exercises/01_strings_exercise.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 1. Fill missing pieces\n", "Fill `____` pieces below to have correct values for `lower_cased`, `stripped` and `stripped_lower_case` variables." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "original = \" Python strings are COOL! \"\n", "lower_cased = original._____\n", "stripped = ____.strip()\n", "stripped_lower_cased = original._____._____" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's verify that the implementation is correct by running the cell below. `assert` will raise `AssertionError` if the statement is not true. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "assert lower_cased == \" python strings are cool! \"\n", "assert stripped == \"Python strings are COOL!\"\n", "assert stripped_lower_cased == \"python strings are cool!\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 2. Prettify ugly string\n", "Use `str` methods to convert `ugly` to wanted `pretty`." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "ugly = \" tiTle of MY new Book\\n\\n\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Your implementation:\n", "pretty = \"TODO\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's make sure that it does what we want. `assert` raises [`AssertionError`](https://docs.python.org/3/library/exceptions.html#AssertionError) if the statement is not `True`." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "print(f\"pretty: {pretty}\")\n", "assert pretty == \"Title Of My New Book\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 3. Format string based on existing variables\n", "Create `sentence` by using `verb`, `language`, and `punctuation` and any other strings you may need." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "verb = \"is\"\n", "language = \"Python\"\n", "punctuation = \"!\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Your implementation:\n", "sentence = \"TODO\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "print(f\"sentence: {sentence}\")\n", "assert sentence == \"Learning Python is fun!\"" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.5.4" } }, "nbformat": 4, "nbformat_minor": 1 } ================================================ FILE: notebooks/beginner/exercises/02_numbers_exercise.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 1. Creating formulas\n", "Write the following mathematical formula in Python:\n", "\n", "\\begin{align}\n", " result = 6a^3 - \\frac{8b^2 }{4c} + 11\n", "\\end{align}\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "a = 2\n", "b = 3\n", "c = 2" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Your formula here:\n", "result = " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "assert result == 50" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 2. Floating point pitfalls\n", "Show that `0.1 + 0.2 == 0.3`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Your solution here\n", "\n", "# This won't work:\n", "# assert 0.1 + 0.2 == 0.3" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.5.4" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/exercises/03_conditionals_exercise.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 1. `if-elif-else`\n", "Fill missing pieces (`____`) of the following code such that prints make sense." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "name = \"John Doe\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "if ____:\n", " print(f'Name \"{name}\" is more than 20 chars long')\n", " length_description = \"long\"\n", "elif ____:\n", " print(f'Name \"{name}\" is more than 15 chars long')\n", " length_description = \"semi long\"\n", "elif ____:\n", " print(f'Name \"{name}\" is more than 10 chars long')\n", " length_description = \"semi long\"\n", "elif ____:\n", " print(f'Name \"{name}\" is 8, 9 or 10 chars long')\n", " length_description = \"semi short\"\n", "else:\n", " print(f'Name \"{name}\" is a short name')\n", " length_description = \"short\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "assert length_description == \"semi short\"" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.5.4" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/exercises/04_lists_exercise.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 1. Fill the missing pieces\n", "Fill the `____` parts of the code below." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Let's create an empty list\n", "my_list = ____\n", "\n", "# Let's add some values\n", "my_list.____(\"Python\")\n", "my_list.____(\"is ok\")\n", "my_list.____(\"sometimes\")\n", "\n", "# Let's remove 'sometimes'\n", "my_list.____(\"sometimes\")\n", "\n", "# Let's change the second item\n", "my_list[____] = \"is neat\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "# Let's verify that it's correct\n", "assert my_list == [\"Python\", \"is neat\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 2. Create a new list without modifiying the original one\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "original = [\"I\", \"am\", \"learning\", \"hacking\", \"in\"]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Your implementation here\n", "modified = " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "assert original == [\"I\", \"am\", \"learning\", \"hacking\", \"in\"]\n", "assert modified == [\"I\", \"am\", \"learning\", \"lists\", \"in\", \"Python\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 3. Create a merged sorted list" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "list1 = [6, 12, 5]\n", "list2 = [6.2, 0, 14, 1]\n", "list3 = [0.9]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Your implementation here\n", "my_list = " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "print(my_list)\n", "assert my_list == [14, 12, 6.2, 6, 5, 1, 0.9, 0]" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.5.4" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/exercises/05_dictionaries_exercise.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 1. Populating a dictionary\n", "Create a dictionary by using all the given variables." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "first_name = \"John\"\n", "last_name = \"Doe\"\n", "favorite_hobby = \"Python\"\n", "sports_hobby = \"gym\"\n", "age = 82" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Your implementation\n", "my_dict = " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "assert my_dict == {\"name\": \"John Doe\", \"age\": 82, \"hobbies\": [\"Python\", \"gym\"]}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 2. Accessing and merging dictionaries\n", "Combine `dict1`, `dict2`, and `dict3` into `my_dict`. In addition, get the value of `special_key` from `my_dict` into a `special_value` variable. Note that original dictionaries should stay untouched and `special_key` should be removed from `my_dict`." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "dict1 = dict(key1=\"This is not that hard\", key2=\"Python is still cool\")\n", "dict2 = {\"key1\": 123, \"special_key\": \"secret\"}\n", "# This is also a away to initialize a dict (list of tuples)\n", "dict3 = dict([(\"key2\", 456), (\"keyX\", \"X\")])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 'Your impelementation'\n", "my_dict = \n", "special_value = " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "assert my_dict == {\"key1\": 123, \"key2\": 456, \"keyX\": \"X\"}\n", "assert special_value == \"secret\"\n", "\n", "# Let's check that the originals are untouched\n", "assert dict1 == {\"key1\": \"This is not that hard\", \"key2\": \"Python is still cool\"}\n", "assert dict2 == {\"key1\": 123, \"special_key\": \"secret\"}\n", "assert dict3 == {\"key2\": 456, \"keyX\": \"X\"}" ] } ], "metadata": { "celltoolbar": "Edit Metadata", "kernelspec": { "display_name": "Python 3", "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.5.4" } }, "nbformat": 4, "nbformat_minor": 1 } ================================================ FILE: notebooks/beginner/exercises/06_for_loops_exercise.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 1. Fill the missing pieces\n", "Fill the `____` parts in the code below." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "words = [\"PYTHON\", \"JOHN\", \"chEEse\", \"hAm\", \"DOE\", \"123\"]\n", "upper_case_words = []\n", "\n", "for ____ in words:\n", " if ____.isupper():\n", " ____.append(____)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "assert upper_case_words == [\"PYTHON\", \"JOHN\", \"DOE\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 2. Calculate the sum of dict values\n", "Calculate the sum of the values in `magic_dict` by taking only into account numeric values (hint: see [isinstance](https://docs.python.org/3/library/functions.html#isinstance)). " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "magic_dict = dict(val1=44, val2=\"secret value\", val3=55.0, val4=1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Your implementation\n", "sum_of_values = " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "assert sum_of_values == 100" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 3. Create a list of strings based on a list of numbers\n", "The rules:\n", "* If the number is a multiple of five and odd, the string should be `'five odd'`\n", "* If the number is a multiple of five and even, the string should be `'five even'`\n", "* If the number is odd, the string is `'odd'`\n", "* If the number is even, the string is `'even'`" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "numbers = [1, 3, 4, 6, 81, 80, 100, 95]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Your implementation\n", "my_list = " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "assert my_list == [\n", " \"odd\",\n", " \"odd\",\n", " \"even\",\n", " \"even\",\n", " \"odd\",\n", " \"five even\",\n", " \"five even\",\n", " \"five odd\",\n", "]" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/exercises/07_functions_exercise.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 1. Fill the missing pieces of the `count_even_numbers` function\n", "Fill `____` pieces of the `count_even_numbers` implemention in order to pass the assertions. You can assume that `numbers` argument is a list of integers." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "____ count_even_numbers(numbers):\n", " count = 0\n", " for num in ____:\n", " if ____ % 2 == ____:\n", " count += ____\n", " _____ _____" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "assert count_even_numbers([1, 2, 3, 4, 5, 6]) == 3\n", "assert count_even_numbers([1, 3, 5, 7]) == 0\n", "assert count_even_numbers([-2, 2, -10, 8]) == 4" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 2. Searching for wanted people\n", "Implement `find_wanted_people` function which takes a list of names (strings) as argument. The function should return a list of names which are present both in `WANTED_PEOPLE` and in the name list given as argument to the function." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "WANTED_PEOPLE = [\"John Doe\", \"Clint Eastwood\", \"Chuck Norris\"]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Your implementation here" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "people_to_check1 = [\"Donald Duck\", \"Clint Eastwood\", \"John Doe\", \"Barack Obama\"]\n", "wanted1 = find_wanted_people(people_to_check1)\n", "assert len(wanted1) == 2\n", "assert \"John Doe\" in wanted1\n", "assert \"Clint Eastwood\" in wanted1\n", "\n", "people_to_check2 = [\"Donald Duck\", \"Mickey Mouse\", \"Zorro\", \"Superman\", \"Robin Hood\"]\n", "wanted2 = find_wanted_people(people_to_check2)\n", "assert wanted2 == []" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 3. Counting average length of words in a sentence\n", "Create a function `average_length_of_words` which takes a string as an argument and returns the average length of the words in the string. You can assume that there is a single space between each word and that the input does not have punctuation. The result should be rounded to one decimal place (hint: see [`round`](https://docs.python.org/3/library/functions.html#round))." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Your implementation here" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "assert average_length_of_words(\"only four lett erwo rdss\") == 4\n", "assert average_length_of_words(\"one two three\") == 3.7\n", "assert average_length_of_words(\"one two three four\") == 3.8\n", "assert average_length_of_words(\"\") == 0" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/exercises/08_testing1_exercise.ipynb ================================================ { "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Let's make sure pytest and ipytest packages are installed\n", "# ipytest is required for running pytest inside Jupyter notebooks\n", "import sys\n", "\n", "!{sys.executable} -m pip install pytest\n", "!{sys.executable} -m pip install ipytest\n", "\n", "# These are needed for running pytest inside Jupyter notebooks\n", "import ipytest\n", "\n", "ipytest.autoconfig()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 1. Creating your first test case\n", "There is an implementation for `get_divisible_by_five` function in the cell below. Your task is to create a test case for this function to verify that it works correctly.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "def get_divisible_by_five(numbers):\n", " \"\"\"Returns a list of numbers which are divisible by five in the list got as an argument\"\"\"\n", " result = []\n", " for num in numbers:\n", " if not num % 5:\n", " result.append(num)\n", "\n", " return result" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%ipytest\n", "\n", "def test_get_divisible_by_five():\n", " # Your implementation here\n" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/exercises/09_recap1_exercise.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 1. Super vowels\n", "Implement `super_vowels` function which takes a string as an argument and returns a modified version of that string. In the return value of `super_vowels`, all vowels should be in upper case whereas all consonants should be in lower case. The vowels are listed in the `VOWELS` variable." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "VOWELS = [\"a\", \"e\", \"i\", \"o\", \"u\"]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Your implementation here" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "assert super_vowels(\"hi wassup!\") == \"hI wAssUp!\"\n", "assert super_vowels(\"HOw aRE You?\") == \"hOw ArE yOU?\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 2. Playing board\n", "Implement `get_playing_board` function which takes an integer as an argument. The function should return a string which resemples a playing board (e.g. a chess board). The board should contain as many rows and columns as requested by the interger argument. See the cell below for examples of desired behavior.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Your implementation here" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "board_of_5 = \" * * \\n\" \"* * *\\n\" \" * * \\n\" \"* * *\\n\" \" * * \\n\"\n", "\n", "board_of_10 = (\n", " \" * * * * *\\n\"\n", " \"* * * * * \\n\"\n", " \" * * * * *\\n\"\n", " \"* * * * * \\n\"\n", " \" * * * * *\\n\"\n", " \"* * * * * \\n\"\n", " \" * * * * *\\n\"\n", " \"* * * * * \\n\"\n", " \" * * * * *\\n\"\n", " \"* * * * * \\n\"\n", ")\n", "\n", "assert get_playing_board(5) == board_of_5\n", "assert get_playing_board(10) == board_of_10\n", "\n", "print(get_playing_board(50))" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/exercises/10_file_io_exercise.ipynb ================================================ { "cells": [ { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "# EXECUTE THIS ONE FIRST!\n", "\n", "from pathlib import Path\n", "\n", "# Constants for the exercises:\n", "WORKING_DIR = Path.cwd()\n", "DATA_DIR = WORKING_DIR.parent / \"data\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 1. Sum numbers listed in a file\n", "Fill ____ pieces of the code below. `sum_numbers_in_file` function takes a input file path as argument, reads the numbers listed in the input file and returns the sum of those numbers. You can assume that each line contains exactly one numeric value." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def sum_numbers_in_file(input_file):\n", " total = 0\n", " with ____(input_file) as ____:\n", " for line in ____:\n", " ____ = line.strip() # Remove potential white space\n", " total += float(____)\n", " return _____" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "in_file = DATA_DIR / \"numbers.txt\"\n", "assert sum_numbers_in_file(in_file) == 189.5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 2. Reading first word from each line of a file\n", "Implement `find_first_words` function which takes an input file path as argument. The function should find the first word of each line in the file and return these words as a list. If a line is empty, the returned list should contain an empty string for that line." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Your implementation here" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "in_file1 = DATA_DIR / \"simple_file.txt\"\n", "in_file2 = DATA_DIR / \"simple_file_with_empty_lines.txt\"\n", "\n", "expected_file_1 = [\"First\", \"Second\", \"Third\", \"And\"]\n", "assert find_first_words(in_file1) == expected_file_1\n", "\n", "expected_file_2 = [\"The\", \"\", \"First\", \"nonsense\", \"\", \"Then\"]\n", "assert find_first_words(in_file2) == expected_file_2" ] } ], "metadata": { "celltoolbar": "Edit Metadata", "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/exercises/11_classes_exercise.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 1. Fill the missing pieces of the `Calculator` class\n", "Fill `____` pieces of the `Calculator` implemention in order to pass the assertions." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class Calculator:\n", " def __init__(self, var1, ____):\n", " self.____ = var1\n", " self.____ = _____\n", "\n", " def calculate_power(self):\n", " return self.____**____.____\n", "\n", " def calculate_sum(____, var3):\n", " return ____.____ + ____.____ + var3" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "calc = Calculator(2, 3)\n", "assert calc.calculate_power() == 8\n", "assert calc.calculate_sum(4) == 9" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 2. Finalize `StringManipulator` class\n", "Fill `____` pieces and create implementation for `stripped_title()`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class StringManipulator:\n", " \"\"\"____\"\"\"\n", " \n", " category = ____\n", " \n", " def __init__(self, original):\n", " self.string = ____\n", " \n", " def reverse_words(self):\n", " words = self.string.____\n", " self.string = ' '.join(reversed(____))\n", " \n", " def make_title(self):\n", " # Create implementation for this\n", " \n", " def get_manipulated(____):\n", " return self._____" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "assert StringManipulator.__doc__ == \"Docstring of StringManipulator\"\n", "assert StringManipulator.category == \"Manipulator\"\n", "\n", "str_manip = StringManipulator(\"cOOL pyThON\")\n", "\n", "str_manip.reverse_words()\n", "assert str_manip.get_manipulated() == \"pyThON cOOL\"\n", "\n", "str_manip.make_title()\n", "assert str_manip.get_manipulated() == \"Python Cool\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 3. Create `Dog` class\n", "Create `Dog` class which has the following specification:\n", "* Dogs consume their energy by barking and gain energy by sleeping\n", "* A fresh `Dog` instance has 10 units of energy\n", "* `Dog` has a method `sleep` which gives 2 units of energy\n", "* `Dog` has a method `bark` which consumes 1 unit of energy\n", "* `Dog` has a method `get_energy` which returns the amount of energy left " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class Dog:\n", " # Your implementation here" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "doge = Dog()\n", "assert doge.get_energy() == 10\n", "\n", "doge.bark()\n", "doge.bark()\n", "doge.bark()\n", "assert doge.get_energy() == 7\n", "\n", "doge.sleep()\n", "assert doge.get_energy() == 9\n", "\n", "another_doge = Dog()\n", "assert another_doge.get_energy() == 10" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/exercises/12_exceptions_exercise.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 1. Dealing with exceptions\n", "Fill `____` parts of the implementation below. `sum_of_list` function takes a list as argument and calculates the sum of values in the list. If some element in the list can not be converted to a numeric value, it should be ignored from the sum." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def sum_of_list(values):\n", " ____ = 0\n", " for val in values:\n", " ____:\n", " numeric_val = float(val)\n", " ____ ____ as e:\n", " ____\n", " ____ += numeric_val\n", " return ____" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "list1 = [1, 2, 3]\n", "list2 = [\"1\", 2.5, \"3.0\"]\n", "list3 = [\"\", \"1\"]\n", "list4 = []\n", "list5 = [\"John\", \"Doe\", \"was\", \"here\"]\n", "nasty_list = [KeyError(), [], dict()]\n", "\n", "assert sum_of_list(list1) == 6\n", "assert sum_of_list(list2) == 6.5\n", "assert sum_of_list(list3) == 1\n", "assert sum_of_list(list4) == 0\n", "assert sum_of_list(list5) == 0\n", "assert sum_of_list(nasty_list) == 0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 2. Using custom exceptions\n", "Implement `verify_short_string` function which takes a single string as argument. If the length of the input string is more than ten characters, the function should raise `TooLongString` exception (note: you have to create `TooLongString` yourself). The function does not have to return anything. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Your implementation here" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "# These should not raise\n", "verify_short_string(\"short\")\n", "verify_short_string(\"10 chars\")\n", "\n", "# This should raise\n", "try:\n", " verify_short_string(\"this is long\")\n", "except TooLongString as e:\n", " # This is ok\n", " pass\n", "else:\n", " # This means that there was no exception\n", " assert False" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/exercises/14_debugging_exercise.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 1. Identifying bugs in code\n", "The following `stripped_reversed_lowercase` function contains at least one bug. You can see this by running the code in the cell below which tests the functionality of the `stripped_reversed_lowercase` function.\n", "\n", "Set trace at the beginning of `stripped_reversed_lowercase` and use debugger to solve the bug(s). Execute the code line by line and print variables used in the function to understand what's going wrong. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def stripped_reversed_lowercase(original):\n", " # Set a breakpoint here and start debugging\n", " # from IPython.core.debugger import Pdb; Pdb().set_trace()\n", " stripped = original.lstrip()\n", " reversed = \" \".join(reversed(stripped))\n", " reversed.lower()\n", " return reversed" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "# Let's verify it works\n", "original = \" \\n Original String \"\n", "result = stripped_reversed_lowercase(original)\n", "assert result == \"gnirts lanigiro\"" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/exercises/15_std_lib1_exercise.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 1. Playing with datetimes\n", "You're given a naive datetime, see `NAIVE_DT` variable below. Although this variable is naive, you happen to know that the time specified by `NAIVE_DT` is in UTC.\n", "\n", "Based on this information, your task is to create new datetime variables by converting `NAIVE_DT` to UTC and then to time in Sydney and Los Angeles. Use the following variable names: `utc_dt`, `sydney_dt`, and `la_dt`. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "import datetime as dt\n", "from zoneinfo import ZoneInfo, available_timezones\n", "\n", "NAIVE_DT = dt.datetime(2000, 1, 1, 10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you don't know the timezone name you're looking for, this may be helpful:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "for tz in available_timezones():\n", " print(tz)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now create `utc_dt`, `sydney_dt`, and `la_dt`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Your implementation here" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's verify that the solution is correct." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "assert utc_dt.isoformat() == \"2000-01-01T10:00:00+00:00\"\n", "assert sydney_dt.isoformat() == \"2000-01-01T21:00:00+11:00\"\n", "assert la_dt.isoformat() == \"2000-01-01T02:00:00-08:00\"\n", "\n", "print(\"All good!\")" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/exercises/16_testing2_exercise.ipynb ================================================ { "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Let's make sure pytest and ipytest packages are installed\n", "# ipytest is required for running pytest inside Jupyter notebooks\n", "import sys\n", "\n", "!{sys.executable} -m pip install pytest\n", "!{sys.executable} -m pip install ipytest\n", "\n", "# These are needed for running pytest inside Jupyter notebooks\n", "import ipytest\n", "import pytest\n", "\n", "ipytest.autoconfig()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 1. Finalize test cases\n", "The testing part of the `TodoList` implementation is incomplete. Fill `____` parts of the tests. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "class TodoNotFound(Exception):\n", " pass\n", "\n", "\n", "class TodoList:\n", " def __init__(self):\n", " self._todo = {}\n", " self._done = {}\n", " self._task_counter = 1\n", "\n", " @property\n", " def todo_tasks(self):\n", " return self._todo\n", "\n", " @property\n", " def done_tasks(self):\n", " return self._done\n", "\n", " def add(self, task):\n", " self._todo[self._task_counter] = task\n", " self._task_counter += 1\n", "\n", " def complete(self, number):\n", " if number not in self._todo:\n", " raise TodoNotFound(f\"{number} not in todos\")\n", "\n", " task = self._todo.pop(number)\n", " self._done[number] = task\n", "\n", " def remove(self, number):\n", " if number not in self._todo:\n", " raise TodoNotFound(f\"{number} not in todos\")\n", "\n", " del self._todo[number]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finalize tests for `TodoList`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%ipytest\n", "\n", "\n", "@pytest.____\n", "def todo_list():\n", " tl = TodoList()\n", " tl.add('buy milk')\n", " tl.add('take dog out')\n", " tl.add('learn pytest fixtures')\n", " ____ ____\n", "\n", "\n", "def test_todo_tasks_property(todo_list):\n", " todo = todo_list.todo_tasks\n", " assert todo == {\n", " 1: 'buy milk',\n", " 2: 'take dog out',\n", " 3: 'learn pytest fixtures'\n", " }\n", "\n", "\n", "def test_add(____):\n", " todo_list.add('check pytest docs')\n", " todos = todo_list.todo_tasks\n", " assert todos[4] == ____\n", "\n", "\n", "def test_complete(todo_list):\n", " # Make sure there is not done tasks yet\n", " assert not todo_list.done_tasks\n", "\n", " todo_list.complete(3)\n", " done = todo_list.____\n", " todo = todo_list.____\n", " assert done[3] == 'learn pytest fixtures'\n", " assert 3 not in ____\n", "\n", "\n", "def test_complete_with_unknown_task_number(todo_list):\n", " # This is how you can test that a certain exception is raised\n", " with pytest.raises(TodoNotFound):\n", " todo_list.complete(10)\n", "\n", "\n", "def test_remove(todo_list):\n", " todo_list.remove(1)\n", " done = todo_list.done_tasks\n", " todo = todo_list.todo_tasks\n", "\n", " assert 1 not in ____\n", " # Make sure it was not moved to done\n", " ____ not done\n", "\n", "\n", "def test_remove_with_unknown_task_number(todo_list):\n", " with pytest.____(____):\n", " todo_list.remove(12)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 2. Testing the [Fibonacci numbers](https://en.wikipedia.org/wiki/Fibonacci_number)\n", "\n", "Implement a test for the `fibonacci` function. Use `pytest.mark.parametrize` and test at least with numbers: 0, 1, 2, 3, and 10. You can find the expected results and more information about the Fibonacci numbers [here](https://en.wikipedia.org/wiki/Fibonacci_number)." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "def fibonacci(number):\n", " if number in [0, 1]:\n", " return number\n", " return fibonacci(number - 1) + fibonacci(number - 2)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%ipytest\n", "\n", "# Your implementation here\n" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/exercises/19_recap2_exercise.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 1. [Rock-paper-scissors](https://en.wikipedia.org/wiki/Rock%E2%80%93paper%E2%80%93scissors) \n", "Implement `rock_paper_scissors` function which takes the player's rock-paper-scissors choice as an input (as integer), randomly selects the choice of the computer and reveals it (prints) and finally announces (prints) the result. The function should return `PLAYER_WINS`, `COMPUTER_WINS` or `TIE`." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "# Constants, you should use these in your implementation\n", "ROCK = 1\n", "PAPER = 2\n", "SCISSORS = 3\n", "\n", "PLAYER_WINS = \"Player wins!! Woop woop!\"\n", "COMPUTER_WINS = \"Robocop wins :-(\"\n", "TIE = \"It's a tie!\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Your implementation here" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once you have finished the implementation of `rock_paper_scissors` function, you can check if it works as expected by playing the game:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def play_rps():\n", " print(\"Welcome to play rock-paper-scissors\")\n", " print(\"The options are:\\nrock: 1\\npaper: 2\\nscissors: 3\")\n", "\n", " result = TIE\n", " while result == TIE:\n", " player_choice = input(\"Give your choice\\n\")\n", "\n", " if not player_choice in [\"1\", \"2\", \"3\"]:\n", " print(\"Invalid choice\")\n", " continue\n", "\n", " result = rock_paper_scissors(int(player_choice))\n", "\n", "\n", "if __name__ == \"__main__\":\n", " play_rps()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you copy the code from above cells into a single .py file, you have a rock-paper-scissor command line game!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 2. Data analyzer\n", "Implement `DataAnalyzer` class which has the following specification:\n", "* `__init__` takes one argument which is a path to the file to be analyzed\n", "* `total_samples` method returns the amount of the data samples in the file\n", "* `average` method returns the average of the data samples in the file\n", "* `median` method returns the median of the data samples in the file\n", "* `max_value` method returns the maximum value of the data samples in the file\n", "* `min_value` method returns the minimum value of the data samples in the file\n", "* `create_report` method returns a report (string) of the file in the following format:\n", "\n", "```\n", "Report for \n", "samples: x\n", "average: x.xx\n", "median: xx.xx\n", "max: xx.xx\n", "min: x.xx\n", "```\n", " \n", "Note that average, median, max, and min should be presented with two decimal places in the report.\n", "\n", "The format of the input file is comma separated and the file contains only numeric values.\n", "\n", "If there is no data in the input file (empty file), `NoData` exception should be raised. Note: `NoData` should be your custom exception." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Your implementation here" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's verify it works." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "from pathlib import Path\n", "\n", "WORKING_DIR = Path.cwd()\n", "DATA_DIR = WORKING_DIR.parent / \"data\"\n", "DATA_FILE = DATA_DIR / \"random_data.txt\"\n", "\n", "da = DataAnalyzer(DATA_FILE)\n", "\n", "assert da.total_samples() == 20\n", "assert da.average() == 49.35\n", "assert da.median() == 47.5\n", "assert da.max_value() == 94\n", "assert da.min_value() == 4\n", "\n", "report = da.create_report()\n", "print(report)\n", "\n", "expected_report = (\n", " \"Report for random_data.txt\\n\"\n", " \"samples: 20\\n\"\n", " \"average: 49.35\\n\"\n", " \"median: 47.50\\n\"\n", " \"max: 94.00\\n\"\n", " \"min: 4.00\"\n", ")\n", "assert report == expected_report" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's check that it raises `NoData` with empty file." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "EMPTY_FILE = DATA_DIR / \"empty_file.txt\"\n", "try:\n", " da_empty = DataAnalyzer(EMPTY_FILE)\n", "except NoData:\n", " print(\"All ok :)\")\n", "else: # There was no exception\n", " assert False" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/html/01_strings.html ================================================ 01_strings ================================================ FILE: notebooks/beginner/html/02_numbers.html ================================================ 02_numbers ================================================ FILE: notebooks/beginner/html/03_conditionals.html ================================================ 03_conditionals ================================================ FILE: notebooks/beginner/html/04_lists.html ================================================ 04_lists ================================================ FILE: notebooks/beginner/html/05_dictionaries.html ================================================ 05_dictionaries ================================================ FILE: notebooks/beginner/html/06_for_loops.html ================================================ 06_for_loops ================================================ FILE: notebooks/beginner/html/07_functions.html ================================================ 07_functions ================================================ FILE: notebooks/beginner/html/08_testing1.html ================================================ 08_testing1 ================================================ FILE: notebooks/beginner/html/10_file_io.html ================================================ 10_file_io ================================================ FILE: notebooks/beginner/html/11_classes.html ================================================ 11_classes ================================================ FILE: notebooks/beginner/html/12_exceptions.html ================================================ 12_exceptions ================================================ FILE: notebooks/beginner/html/13_modules_and_packages.html ================================================ 13_modules_and_packages ================================================ FILE: notebooks/beginner/html/14_debugging.html ================================================ 14_debugging ================================================ FILE: notebooks/beginner/html/15_std_lib.html ================================================ 15_std_lib ================================================ FILE: notebooks/beginner/html/16_testing2.html ================================================ 16_testing2 ================================================ FILE: notebooks/beginner/html/17_venv.html ================================================ 17_venv ================================================ FILE: notebooks/beginner/html/18_project_structure.html ================================================ 18_project_structure ================================================ FILE: notebooks/beginner/notebooks/01_strings.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# [Strings](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_string = \"Python is my favorite programming language!\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_string" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "type(my_string)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "len(my_string)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Respecting [PEP8](https://www.python.org/dev/peps/pep-0008/#maximum-line-length) with long strings" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "long_story = (\n", " \"Lorem ipsum dolor sit amet, consectetur adipiscing elit.\"\n", " \"Pellentesque eget tincidunt felis. Ut ac vestibulum est.\"\n", " \"In sed ipsum sit amet sapien scelerisque bibendum. Sed \"\n", " \"sagittis purus eu diam fermentum pellentesque.\"\n", ")\n", "long_story" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `str.replace()`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you don't know how it works, you can always check the `help`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "help(str.replace)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This will not modify `my_string` because replace is not done in-place." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_string.replace(\"a\", \"?\")\n", "print(my_string)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You have to store the return value of `replace` instead." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_modified_string = my_string.replace(\"is\", \"will be\")\n", "print(my_modified_string)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## f-strings" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "first_name = \"John\"\n", "last_name = \"Doe\"\n", "age = 88\n", "print(f\"My name is {first_name} {last_name}, you can call me {first_name}.\")\n", "print(f\"I'm {age} years old.\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(f\"Use '=' to also print the variable name like this: {age=}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `str.join()`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pandas = \"pandas\"\n", "numpy = \"numpy\"\n", "requests = \"requests\"\n", "cool_python_libs = \", \".join([pandas, numpy, requests])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(f\"Some cool python libraries: {cool_python_libs}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternative (not as [Pythonic](http://docs.python-guide.org/en/latest/writing/style/#idioms) and [slower](https://waymoot.org/home/python_string/)):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cool_python_libs = pandas + \", \" + numpy + \", \" + requests\n", "print(f\"Some cool python libraries: {cool_python_libs}\")\n", "\n", "cool_python_libs = pandas\n", "cool_python_libs += \", \" + numpy\n", "cool_python_libs += \", \" + requests\n", "print(f\"Some cool python libraries: {cool_python_libs}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `str.upper(), str.lower(), str.title()`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mixed_case = \"PyTHoN hackER\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mixed_case.upper()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mixed_case.lower()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mixed_case.title()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `str.strip()`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ugly_formatted = \" \\n \\t Some story to tell \"\n", "stripped = ugly_formatted.strip()\n", "\n", "print(f\"ugly: {ugly_formatted}\")\n", "print(f\"stripped: {stripped}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `str.split()`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sentence = \"three different words\"\n", "words = sentence.split()\n", "print(words)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "type(words)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "secret_binary_data = \"01001,101101,11100000\"\n", "binaries = secret_binary_data.split(\",\")\n", "print(binaries)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Calling multiple methods in a row" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ugly_mixed_case = \" ThIS LooKs BAd \"\n", "pretty = ugly_mixed_case.strip().lower().replace(\"bad\", \"good\")\n", "print(pretty)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that execution order is from left to right. Thus, this won't work:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pretty = ugly_mixed_case.replace(\"bad\", \"good\").strip().lower()\n", "print(pretty)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## [Escape characters](http://python-reference.readthedocs.io/en/latest/docs/str/escapes.html#escape-characters)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "two_lines = \"First line\\nSecond line\"\n", "print(two_lines)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "indented = \"\\tThis will be indented\"\n", "print(indented)" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 1 } ================================================ FILE: notebooks/beginner/notebooks/02_numbers.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# [Numbers](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `int`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_int = 6\n", "print(f\"value: {my_int}, type: {type(my_int)}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `float`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_float = float(my_int)\n", "print(f\"value: {my_float}, type: {type(my_float)}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that division of `int`s produces `float`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(1 / 1)\n", "print(6 / 5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Be aware of the binary floating-point pitfalls (see [Decimal](#decimal) for workaround):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "val = 0.1 + 0.1 + 0.1\n", "print(val == 0.3)\n", "print(val)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Floor division `//`, modulus `%`, power `**`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "7 // 5" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "7 % 5" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "2**3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "## [`decimal.Decimal`](https://docs.python.org/3/library/decimal.html)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from decimal import Decimal" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from_float = Decimal(0.1)\n", "from_str = Decimal(\"0.1\")\n", "print(f\"from float: {from_float}\\nfrom string: {from_str}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_decimal = Decimal(\"0.1\")\n", "sum_of_decimals = my_decimal + my_decimal + my_decimal\n", "print(sum_of_decimals == Decimal(\"0.3\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Operator precedence in calculations\n", "Mathematical operator precedence applies. Use brackets if you want to change the execution order:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(1 + 2**2 * 3 / 6) # 1 + 4 * 3 / 6 == 1 + 12 / 6 == 1 + 2\n", "print((1 + 2**2) * 3 / 6)" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/notebooks/03_conditionals.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Conditionals" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Testing truth value" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `bool`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(f\"type of True and False: {type(True)}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(f\"0: {bool(0)}, 1: {bool(1)}\")\n", "print(f\"empty list: {bool([])}, list with values: {bool(['woop'])}\")\n", "print(f\"empty dict: {bool({})}, dict with values: {bool({'Python': 'cool'})}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `==, !=, >, <, >=, <=`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(f\"{1 == 0}\")\n", "print(f\"{1 != 0}\")\n", "print(f\"{1 > 0}\")\n", "print(f\"{1 > 1}\")\n", "print(f\"{1 < 0}\")\n", "print(f\"{1 < 1}\")\n", "print(f\"{1 >= 0}\")\n", "print(f\"{1 >= 1}\")\n", "print(f\"{1 <= 0}\")\n", "print(f\"{1 <= 1}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can combine these:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(f\"{1 <= 2 <= 3}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `and, or, not`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "python_is_cool = True\n", "java_is_cool = False\n", "empty_list = []\n", "secret_value = 3.14" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(f\"Python and java are both cool: {python_is_cool and java_is_cool}\")\n", "print(f\"secret_value and python_is_cool: {secret_value and python_is_cool}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(f\"Python or java is cool: {python_is_cool or java_is_cool}\")\n", "print(f\"{1 >= 1.1 or 2 < 1.4}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(f\"Java is not cool: {not java_is_cool}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can combine multiple statements, execution order is from left to right. You can control the execution order by using brackets." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(bool(not java_is_cool or secret_value and python_is_cool or empty_list))\n", "print(bool(not (java_is_cool or secret_value and python_is_cool or empty_list)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `if`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "statement = True\n", "if statement:\n", " print(\"statement is True\")\n", "\n", "if not statement:\n", " print(\"statement is not True\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "empty_list = []\n", "# With if and elif, conversion to `bool` is implicit\n", "if empty_list:\n", " print(\"empty list will not evaluate to True\") # this won't be executed" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "val = 3\n", "if 0 <= val < 1 or val == 3:\n", " print(\"Value is positive and less than one or value is three\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `if-else`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_dict = {}\n", "if my_dict:\n", " print(\"there is something in my dict\")\n", "else:\n", " print(\"my dict is empty :(\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `if-elif-else`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "val = 88\n", "if val >= 100:\n", " print(\"value is equal or greater than 100\")\n", "elif val > 10:\n", " print(\"value is greater than 10 but less than 100\")\n", "else:\n", " print(\"value is equal or less than 10\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can have as many `elif` statements as you need. In addition, `else` at the end is not mandatory." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "greeting = \"Hello fellow Pythonista!\"\n", "language = \"Italian\"\n", "\n", "if language == \"Swedish\":\n", " greeting = \"Hejsan!\"\n", "elif language == \"Finnish\":\n", " greeting = \"Latua perkele!\"\n", "elif language == \"Spanish\":\n", " greeting = \"Hola!\"\n", "elif language == \"German\":\n", " greeting = \"Guten Tag!\"\n", "\n", "print(greeting)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For more detailed overview about conditionals, check this [tutorial from Real Python](https://realpython.com/python-conditional-statements/)." ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 1 } ================================================ FILE: notebooks/beginner/notebooks/04_lists.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# [Lists](https://docs.python.org/3/library/stdtypes.html#lists)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_empty_list = []\n", "print(f\"empty list: {my_empty_list}, type: {type(my_empty_list)}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "list_of_ints = [1, 2, 6, 7]\n", "list_of_misc = [0.2, 5, \"Python\", \"is\", \"still fun\", \"!\"]\n", "print(f\"lengths: {len(list_of_ints)} and {len(list_of_misc)}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Accessing values" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_list = [\"Python\", \"is\", \"still\", \"cool\"]\n", "print(my_list[0])\n", "print(my_list[3])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "coordinates = [[12.0, 13.3], [0.6, 18.0], [88.0, 1.1]] # two dimensional\n", "print(f\"first coordinate: {coordinates[0]}\")\n", "print(f\"second element of first coordinate: {coordinates[0][1]}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Updating values" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_list = [0, 1, 2, 3, 4, 5]\n", "my_list[0] = 99\n", "print(my_list)\n", "\n", "# remove first value\n", "del my_list[0]\n", "print(my_list)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Checking if certain value is present in list" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "languages = [\"Java\", \"C++\", \"Go\", \"Python\", \"JavaScript\"]\n", "if \"Python\" in languages:\n", " print(\"Python is there!\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "if 6 not in [1, 2, 3, 7]:\n", " print(\"number 6 is not present\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## List are mutable" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "original = [1, 2, 3]\n", "modified = original\n", "modified[0] = 99\n", "print(f\"original: {original}, modified: {modified}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can get around this by creating new `list`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "original = [1, 2, 3]\n", "modified = list(original) # Note list()\n", "# Alternatively, you can use copy method\n", "# modified = original.copy()\n", "modified[0] = 99\n", "print(f\"original: {original}, modified: {modified}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `list.append()`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_list = [1]\n", "my_list.append(\"ham\")\n", "print(my_list)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `list.remove()`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_list = [\"Python\", \"is\", \"sometimes\", \"fun\"]\n", "my_list.remove(\"sometimes\")\n", "print(my_list)\n", "\n", "# If you are not sure that the value is in list, better to check first:\n", "if \"Java\" in my_list:\n", " my_list.remove(\"Java\")\n", "else:\n", " print(\"Java is not part of this story.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `list.sort()`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "numbers = [8, 1, 6, 5, 10]\n", "numbers.sort()\n", "print(f\"numbers: {numbers}\")\n", "\n", "numbers.sort(reverse=True)\n", "print(f\"numbers reversed: {numbers}\")\n", "\n", "words = [\"this\", \"is\", \"a\", \"list\", \"of\", \"words\"]\n", "words.sort()\n", "print(f\"words: {words}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `sorted(list)`\n", "While `list.sort()` sorts the list in-place, `sorted(list)` returns a new list and leaves the original untouched:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "numbers = [8, 1, 6, 5, 10]\n", "sorted_numbers = sorted(numbers)\n", "print(f\"{numbers=}, {sorted_numbers=}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `list.extend()`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "first_list = [\"beef\", \"ham\"]\n", "second_list = [\"potatoes\", 1, 3]\n", "first_list.extend(second_list)\n", "print(f\"{first_list=}, {second_list=}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively you can also extend lists by summing them:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "first = [1, 2, 3]\n", "second = [4, 5]\n", "first += second # same as: first = first + second\n", "print(f\"{first=}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `list.reverse()`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_list = [\"a\", \"b\", \"ham\"]\n", "my_list.reverse()\n", "print(my_list)" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/notebooks/05_dictionaries.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# [Dictionaries](https://docs.python.org/3/library/stdtypes.html#dict) \n", "Collections of `key`-`value` pairs. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_empty_dict = {} # alternative: my_empty_dict = dict()\n", "print(f\"dict: {my_empty_dict}, type: {type(my_empty_dict)}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Initialization" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "dict1 = {\"value1\": 1.6, \"value2\": 10, \"name\": \"John Doe\"}\n", "dict2 = dict(value1=1.6, value2=10, name=\"John Doe\")\n", "\n", "print(dict1)\n", "print(dict2)\n", "\n", "print(f\"equals: {dict1 == dict2}\")\n", "print(f\"length: {len(dict1)}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `dict.keys(), dict.values(), dict.items()`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(f\"keys: {dict1.keys()}\")\n", "print(f\"values: {dict1.values()}\")\n", "print(f\"items: {dict1.items()}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Accessing and setting values" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_dict = {}\n", "my_dict[\"key1\"] = \"value1\"\n", "my_dict[\"key2\"] = 99\n", "my_dict[\"key1\"] = \"new value\" # overriding existing value\n", "print(my_dict)\n", "print(f\"value of key1: {my_dict['key1']}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Accessing a nonexistent key will raise `KeyError` (see [`dict.get()`](#dict_get) for workaround):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# print(my_dict['nope'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Deleting" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_dict = {\"key1\": \"value1\", \"key2\": 99, \"keyX\": \"valueX\"}\n", "del my_dict[\"keyX\"]\n", "print(my_dict)\n", "\n", "# Usually better to make sure that the key exists (see also pop() and popitem())\n", "key_to_delete = \"my_key\"\n", "if key_to_delete in my_dict:\n", " del my_dict[key_to_delete]\n", "else:\n", " print(f\"{key_to_delete} is not in {my_dict}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Dictionaries are mutable" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_dict = {\"ham\": \"good\", \"carrot\": \"semi good\"}\n", "my_other_dict = my_dict\n", "my_other_dict[\"carrot\"] = \"super tasty\"\n", "my_other_dict[\"sausage\"] = \"best ever\"\n", "print(f\"{my_dict=}\\nother: {my_other_dict}\")\n", "print(f\"equals: {my_dict == my_other_dict}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Create a new `dict` if you want to have a copy:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_dict = {\"ham\": \"good\", \"carrot\": \"semi good\"}\n", "my_other_dict = dict(my_dict)\n", "my_other_dict[\"beer\"] = \"decent\"\n", "print(f\"{my_dict=}\\nother: {my_other_dict}\")\n", "print(f\"equals: {my_dict == my_other_dict}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "## `dict.get()`\n", "Returns `None` if `key` is not in `dict`. However, you can also specify `default` return value which will be returned if `key` is not present in the `dict`. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_dict = {\"a\": 1, \"b\": 2, \"c\": 3}\n", "value_of_d = my_dict.get(\"d\")\n", "print(f\"d: {value_of_d}\")\n", "\n", "value_of_d = my_dict.get(\"d\", \"my default value\")\n", "print(f\"d: {value_of_d}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `dict.pop()`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_dict = dict(food=\"ham\", drink=\"beer\", sport=\"football\")\n", "print(f\"dict before pops: {my_dict}\")\n", "\n", "food = my_dict.pop(\"food\")\n", "print(f\"food: {food}\")\n", "print(f\"dict after popping food: {my_dict}\")\n", "\n", "food_again = my_dict.pop(\"food\", \"default value for food\")\n", "print(f\"food again: {food_again}\")\n", "print(f\"dict after popping food again: {my_dict}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `dict.setdefault()`\n", "Returns the `value` of `key` defined as first parameter. If the `key` is not present in the dict, adds `key` with default value (second parameter)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_dict = {\"a\": 1, \"b\": 2, \"c\": 3}\n", "a = my_dict.setdefault(\"a\", \"my default value\")\n", "d = my_dict.setdefault(\"d\", \"my default value\")\n", "print(f\"a: {a}\\nd: {d}\\nmy_dict: {my_dict}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `dict.update()`\n", "Merge two `dict`s" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "dict1 = {\"a\": 1, \"b\": 2}\n", "dict2 = {\"c\": 3}\n", "dict1.update(dict2)\n", "print(dict1)\n", "\n", "# If they have same keys:\n", "dict1.update({\"c\": 4})\n", "print(dict1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The keys of a `dict` have to be immutable" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Thus you can not use e.g. a `list` or a `dict` as key because they are mutable types\n", ":" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# bad_dict = {['my_list'], 'value'} # Raises TypeError" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Values can be mutable" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "good_dict = {\"my key\": [\"Python\", \"is\", \"still\", \"cool\"]}\n", "print(good_dict)" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/notebooks/06_for_loops.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# [`for` loops](https://docs.python.org/3/tutorial/controlflow.html#for-statements)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Looping lists" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_list = [1, 2, 3, 4, \"Python\", \"is\", \"neat\"]\n", "for item in my_list:\n", " print(item)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `break`\n", "Stop the execution of the loop." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for item in my_list:\n", " if item == \"Python\":\n", " break\n", " print(item)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `continue`\n", "Continue to the next item without executing the lines occuring after `continue` inside the loop." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for item in my_list:\n", " if item == 1:\n", " continue\n", " print(item)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `enumerate()`\n", "In case you need to also know the index:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for idx, val in enumerate(my_list):\n", " print(f\"idx: {idx}, value: {val}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Looping dictionaries" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_dict = {\"hacker\": True, \"age\": 72, \"name\": \"John Doe\"}\n", "for val in my_dict:\n", " print(val)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for key, val in my_dict.items():\n", " print(f\"{key}={val}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `range()`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for number in range(5):\n", " print(number)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for number in range(2, 5):\n", " print(number)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for number in range(0, 10, 2): # last one is step\n", " print(number)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.5.4" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/notebooks/07_functions.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Functions" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def my_first_function():\n", " print(\"Hello world!\")\n", "\n", "\n", "print(f\"type: {type(my_first_function)}\")\n", "\n", "my_first_function() # Calling a function" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Arguments" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def greet_us(name1, name2):\n", " print(f\"Hello {name1} and {name2}!\")\n", "\n", "\n", "greet_us(\"John Doe\", \"Superman\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Function with return value\n", "def strip_and_lowercase(original):\n", " modified = original.strip().lower()\n", " return modified\n", "\n", "\n", "uggly_string = \" MixED CaSe \"\n", "pretty = strip_and_lowercase(uggly_string)\n", "print(f\"pretty: {pretty}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Keyword arguments" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def my_fancy_calculation(first, second, third):\n", " return first + second - third\n", "\n", "\n", "print(my_fancy_calculation(3, 2, 1))\n", "\n", "print(my_fancy_calculation(first=3, second=2, third=1))\n", "\n", "# With keyword arguments you can mix the order\n", "print(my_fancy_calculation(third=1, first=3, second=2))\n", "\n", "# You can mix arguments and keyword arguments but you have to start with arguments\n", "print(my_fancy_calculation(3, third=1, second=2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Default arguments" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def create_person_info(name, age, job=None, salary=300):\n", " info = {\"name\": name, \"age\": age, \"salary\": salary}\n", "\n", " # Add 'job' key only if it's provided as parameter\n", " if job:\n", " info.update(dict(job=job))\n", "\n", " return info\n", "\n", "\n", "person1 = create_person_info(\"John Doe\", 82) # use default values for job and salary\n", "person2 = create_person_info(\"Lisa Doe\", 22, \"hacker\", 10000)\n", "print(person1)\n", "print(person2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Don't use mutable objects as default arguments!**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def append_if_multiple_of_five(number, magical_list=[]):\n", " if number % 5 == 0:\n", " magical_list.append(number)\n", " return magical_list\n", "\n", "\n", "print(append_if_multiple_of_five(100))\n", "print(append_if_multiple_of_five(105))\n", "print(append_if_multiple_of_five(123))\n", "print(append_if_multiple_of_five(123, []))\n", "print(append_if_multiple_of_five(123))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here's how you can achieve desired behavior:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def append_if_multiple_of_five(number, magical_list=None):\n", " if not magical_list:\n", " magical_list = []\n", " if number % 5 == 0:\n", " magical_list.append(number)\n", " return magical_list\n", "\n", "\n", "print(append_if_multiple_of_five(100))\n", "print(append_if_multiple_of_five(105))\n", "print(append_if_multiple_of_five(123))\n", "print(append_if_multiple_of_five(123, []))\n", "print(append_if_multiple_of_five(123))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Docstrings\n", "Strings for documenting your functions, methods, modules and variables." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def print_sum(val1, val2):\n", " \"\"\"Function which prints the sum of given arguments.\"\"\"\n", " print(f\"sum: {val1 + val2}\")\n", "\n", "\n", "print(help(print_sum))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def calculate_sum(val1, val2):\n", " \"\"\"This is a longer docstring defining also the args and the return value.\n", "\n", " Args:\n", " val1: The first parameter.\n", " val2: The second parameter.\n", "\n", " Returns:\n", " The sum of val1 and val2.\n", "\n", " \"\"\"\n", " return val1 + val2\n", "\n", "\n", "print(help(calculate_sum))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [`pass`](https://docs.python.org/3/reference/simple_stmts.html#the-pass-statement) statement\n", "`pass` is a statement which does nothing when it's executed. It can be used e.g. a as placeholder to make the code syntatically correct while sketching the functions and/or classes of your application. For example, the following is valid Python. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def my_function(some_argument):\n", " pass\n", "\n", "\n", "def my_other_function():\n", " pass" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/notebooks/08_testing1.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Testing with [pytest](https://docs.pytest.org/en/latest/) - part 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Why to write tests?\n", "* Who wants to perform manual testing?\n", "* When you fix a bug or add a new feature, tests are a way to verify that you did not break anything on the way\n", "* If you have clear requirements, you can have matching test(s) for each requirement\n", "* You don't have to be afraid of refactoring\n", "* Tests document your implementation - they show other people use cases of your implementation\n", "* This list is endless..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## [Test-driven development](https://en.wikipedia.org/wiki/Test-driven_development) aka TDD\n", "In short, the basic idea of TDD is to write tests before writing the actual implementation. Maybe the most significant benefit of the approach is that the developer focuses on writing tests which match with what the program should do. Whereas if the tests are written after the actual implementation, there is a high risk for rushing tests which just show green light for the already written logic.\n", "\n", "Tests are first class citizens in modern, agile software development, which is why it's important to start thinking TDD early during your Python learning path. \n", "\n", "The workflow of TDD can be summarized as follows:\n", "1. Add a test case(s) for the change / feature / bug fix you are going to implement\n", "2. Run all tests and check that the new one fails\n", "3. Implement required changes\n", "4. Run tests and verify that all pass\n", "5. Refactor" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Running pytest inside notebooks\n", "These are the steps required to run pytest inside Jupyter cells. You can copy the content of this cell to the top of your notebook which contains tests." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Let's make sure pytest and ipytest packages are installed\n", "# ipytest is required for running pytest inside Jupyter notebooks\n", "import sys\n", "\n", "!{sys.executable} -m pip install pytest\n", "!{sys.executable} -m pip install ipytest\n", "\n", "# These are needed for running pytest inside Jupyter notebooks\n", "import ipytest\n", "\n", "ipytest.autoconfig()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `pytest` test cases\n", "Let's consider we have a function called `sum_of_three_numbers` for which we want to write a test." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def sum_of_three_numbers(num1, num2, num3):\n", " return num1 + num2 + num3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pytest test cases are actually quite similar as you have already seen in the exercises. Most of the exercises are structured like pytest test cases by dividing each exercise into three cells:\n", "1. Setup the variables used in the test\n", "2. Your implementation\n", "3. Verify that your implementation does what is wanted by using assertions\n", "\n", "See the example test case below to see the similarities between the exercises and common structure of test cases." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%ipytest\n", "\n", "def test_sum_of_three_numbers():\n", " # 1. Setup the variables used in the test\n", " num1 = 2\n", " num2 = 3\n", " num3 = 5\n", " \n", " # 2. Call the functionality you want to test\n", " result = sum_of_three_numbers(num1, num2, num3)\n", " \n", " # 3. Verify that the outcome is expected\n", " assert result == 10" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now go ahead and change the line `assert result == 10` such that the assertion fails to see the output of a failed test." ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/notebooks/10_file_io.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# [File I/O](https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files)\n", "Reading and writing files." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Working with paths" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from pathlib import Path\n", "\n", "current_file = Path(\"file_io.ipynb\").resolve()\n", "print(f\"current file: {current_file}\")\n", "# Note: in .py files you can get the path of current file by Path(__file__)\n", "\n", "current_dir = current_file.parent\n", "print(f\"current directory: {current_dir}\")\n", "\n", "data_dir = current_dir.parent / \"data\"\n", "print(f\"data directory: {data_dir}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Checking if path exists" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(f\"exists: {data_dir.exists()}\")\n", "print(f\"is file: {data_dir.is_file()}\")\n", "print(f\"is directory: {data_dir.is_dir()}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Reading files" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "file_path = data_dir / \"simple_file.txt\"\n", "\n", "with open(file_path) as simple_file:\n", " for line in simple_file:\n", " print(line.strip())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The [`with`](https://docs.python.org/3/reference/compound_stmts.html#the-with-statement) statement is for obtaining a [context manager](https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers) that will be used as an execution context for the commands inside the `with`. Context managers guarantee that certain operations are done when exiting the context. \n", "\n", "In this case, the context manager guarantees that `simple_file.close()` is implicitly called when exiting the context. This is a way to make developers life easier: you don't have to remember to explicitly close the file you openened nor be worried about an exception occuring while the file is open. Unclosed file maybe a source of a resource leak. Thus, prefer using `with open()` structure always with file I/O.\n", "\n", "To have an example, the same as above without the `with`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "file_path = data_dir / \"simple_file.txt\"\n", "\n", "# THIS IS NOT THE PREFERRED WAY\n", "simple_file = open(file_path)\n", "for line in simple_file:\n", " print(line.strip())\n", "simple_file.close() # This has to be called explicitly" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Writing files" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "new_file_path = data_dir / \"new_file.txt\"\n", "\n", "with open(new_file_path, \"w\") as my_file:\n", " my_file.write(\"This is my first file that I wrote with Python.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now go and check that there is a new_file.txt in the data directory. After that you can delete the file by:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "if new_file_path.exists(): # make sure it's there\n", " new_file_path.unlink()" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/notebooks/11_classes.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# [Classes](https://docs.python.org/3/tutorial/classes.html#a-first-look-at-classes)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class MyFirstClass:\n", " def __init__(self, name):\n", " self.name = name\n", "\n", " def greet(self):\n", " print(f\"Hello {self.name}!\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_instance = MyFirstClass(\"John Doe\")\n", "print(f\"my_instance: {my_instance}\")\n", "print(f\"type: {type(my_instance)}\")\n", "print(f\"my_instance.name: {my_instance.name}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Methods\n", "The functions inside classes are called methods. They are used similarly as functions. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "alice = MyFirstClass(name=\"Alice\")\n", "alice.greet()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `__init__()`\n", "`__init__()` is a special method that is used for initialising instances of the class. It's called when you create an instance of the class. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class Example:\n", " def __init__(self):\n", " print(\"Now we are inside __init__\")\n", "\n", "\n", "print(\"creating instance of Example\")\n", "example = Example()\n", "print(\"instance created\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`__init__()` is typically used for initialising instance variables of your class. These can be listed as arguments after `self`. To be able to access these instance variables later during your instance's lifetime, you have to save them into `self`. `self` is the first argument of the methods of your class and it's your access to the instance variables and other methods. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class Example:\n", " def __init__(self, var1, var2):\n", " self.first_var = var1\n", " self.second_var = var2\n", "\n", " def print_variables(self):\n", " print(f\"{self.first_var} {self.second_var}\")\n", "\n", "\n", "e = Example(\"abc\", 123)\n", "e.print_variables()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `__str__()`\n", "`__str__()` is a special method which is called when an instance of the class is converted to string (e.g. when you want to print the instance). In other words, by defining `__str__` method for your class, you can decide what's the printable version of the instances of your class. The method should return a string." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class Person:\n", " def __init__(self, name, age):\n", " self.name = name\n", " self.age = age\n", "\n", " def __str__(self):\n", " return f\"Person: {self.name}\"\n", "\n", "\n", "jack = Person(\"Jack\", 82)\n", "print(f\"This is the string presentation of jack: {jack}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Class variables vs instance variables\n", "Class variables are shared between all the instances of that class whereas instance variables can hold different values between different instances of that class." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class Example:\n", " # These are class variables\n", " name = \"Example class\"\n", " description = \"Just an example of a simple class\"\n", "\n", " def __init__(self, var1):\n", " # This is an instance variable\n", " self.instance_variable = var1\n", "\n", " def show_info(self):\n", " info = f\"instance_variable: {self.instance_variable}, name: {Example.name}, description: {Example.description}\"\n", " print(info)\n", "\n", "\n", "inst1 = Example(\"foo\")\n", "inst2 = Example(\"bar\")\n", "\n", "# name and description have identical values between instances\n", "assert inst1.name == inst2.name == Example.name\n", "assert inst1.description == inst2.description == Example.description\n", "\n", "# If you change the value of a class variable, it's changed across all instances\n", "Example.name = \"Modified name\"\n", "inst1.show_info()\n", "inst2.show_info()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Public vs private\n", "In python there's now strict separation for private/public methods or instance variables. The convention is to start the name of the method or instance variable with underscore if it should be treated as private. Private means that it should not be accessed from outside of the class.\n", "\n", "For example, let's consider that we have a `Person` class which has `age` as an instance variable. We want that `age` is not directly accessed (e.g. changed) after the instance is created. In Python, this would be:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class Person:\n", " def __init__(self, age):\n", " self._age = age\n", "\n", "\n", "example_person = Person(age=15)\n", "# You can't do this:\n", "# print(example_person.age)\n", "# Nor this:\n", "# example_person.age = 16" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you want the `age` to be readable but not writable, you can use `property`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class Person:\n", " def __init__(self, age):\n", " self._age = age\n", "\n", " @property\n", " def age(self):\n", " return self._age\n", "\n", "\n", "example_person = Person(age=15)\n", "# Now you can do this:\n", "print(example_person.age)\n", "# But not this:\n", "# example_person.age = 16" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This way you can have a controlled access to the instance variables of your class: " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class Person:\n", " def __init__(self, age):\n", " self._age = age\n", "\n", " @property\n", " def age(self):\n", " return self._age\n", "\n", " def celebrate_birthday(self):\n", " self._age += 1\n", " print(f\"Happy bday for {self._age} years old!\")\n", "\n", "\n", "example_person = Person(age=15)\n", "example_person.celebrate_birthday()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Introduction to inheritance" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class Animal:\n", " def greet(self):\n", " print(\"Hello, I am an animal\")\n", "\n", " @property\n", " def favorite_food(self):\n", " return \"beef\"\n", "\n", "\n", "class Dog(Animal):\n", " def greet(self):\n", " print(\"wof wof\")\n", "\n", "\n", "class Cat(Animal):\n", " @property\n", " def favorite_food(self):\n", " return \"fish\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "dog = Dog()\n", "dog.greet()\n", "print(f\"Dog's favorite food is {dog.favorite_food}\")\n", "\n", "cat = Cat()\n", "cat.greet()\n", "print(f\"Cat's favorite food is {cat.favorite_food}\")" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/notebooks/12_exceptions.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# [Exceptions](https://docs.python.org/3/library/exceptions.html#concrete-exceptions)\n", "When something goes wrong an exception is raised. For example, if you try to divide by zero, `ZeroDivisionError` is raised or if you try to access a nonexistent key in a dictionary, `KeyError` is raised.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "empty_dict = {}\n", "# empty_dict['key'] # Uncomment to see the traceback" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `try-except` structure \n", "If you know that a block of code can fail in some manner, you can use `try-except` structure to handle potential exceptions in a desired way." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Let's try to open a file that does not exist\n", "file_name = \"not_existing.txt\"\n", "\n", "try:\n", " with open(file_name) as my_file:\n", " print(\"File is successfully open\")\n", "\n", "except FileNotFoundError as e:\n", " print(f\"Uups, file: {file_name} not found\")\n", " print(f\"Exception: {e} was raised\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you don't know the type of exceptions that a code block can possibly raise, you can use `Exception` which catches all exceptions. In addition, you can have multiple `except` statements." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def calculate_division(var1, var2):\n", " result = 0\n", "\n", " try:\n", " result = var1 / var2\n", " except ZeroDivisionError as ex1:\n", " print(\"Can't divide by zero\")\n", " except Exception as ex2:\n", " print(f\"Exception: {ex2}\")\n", "\n", " return result\n", "\n", "\n", "result1 = calculate_division(3, 3)\n", "print(f\"result1: {result1}\")\n", "\n", "result2 = calculate_division(3, \"3\")\n", "print(f\"result2: {result2}\")\n", "\n", "result3 = calculate_division(3, 0)\n", "print(f\"result3: {result3}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`try-except` can be also in outer scope:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def calculate_division(var1, var2):\n", " return var1 / var2\n", "\n", "\n", "try:\n", " result = calculate_division(3, \"3\")\n", "except Exception as e:\n", " print(e)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating your custom exceptions\n", "In your own applications, you can use custom exceptions for signaling users about errors which occur during your application run time. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import math\n", "\n", "\n", "# Define your own exception\n", "class NegativeNumbersNotSupported(Exception):\n", " pass\n", "\n", "\n", "# Dummy example how to use your custom exception\n", "def secret_calculation(number1, number2):\n", " if number1 < 0 or number2 < 0:\n", " msg = f\"Negative number in at least one of the parameters: {number1}, {number2}\"\n", " raise NegativeNumbersNotSupported(msg)\n", "\n", " return math.sqrt(number1) + math.sqrt(number2)\n", "\n", "\n", "# Uncomment to see the traceback\n", "# result = secret_calculation(-1, 1)" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 1 } ================================================ FILE: notebooks/beginner/notebooks/13_modules_and_packages.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# [Modules and packages](https://docs.python.org/3/tutorial/modules.html#modules)\n", "\n", "> Module is a Python source code file, i.e. a file with .py extension.\n", "\n", "> Package is a directory which contains `__init__.py` file and can contain python modules and other packages. \n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Why to organize your code into modules and packages\n", "* Maintainability\n", "* Reusability\n", "* Namespacing\n", "* People unfamiliar with your project can get a clear overview just by looking at the directory structure of your project\n", "* Searching for certain functionality or class is easy" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## How to use\n", "\n", "Let's use the following directory structure as an example:\n", "\n", " \n", "```\n", "food_store/\n", " __init__.py\n", " \n", " product/\n", " __init__.py\n", " \n", " fruit/\n", " __init__.py\n", " apple.py\n", " banana.py\n", " \n", " drink/\n", " __init__.py\n", " juice.py\n", " milk.py\n", " beer.py\n", "\n", " cashier/\n", " __ini__.py\n", " receipt.py\n", " calculator.py\n", "```\n", "\n", "\n", "Let's consider that banana.py file contains:\n", "\n", "```python\n", "\n", "def get_available_brands():\n", " return [\"chiquita\"]\n", "\n", "\n", "class Banana:\n", " def __init__(self, brand=\"chiquita\"):\n", " if brand not in get_available_brands():\n", " raise ValueError(f\"Unknown brand: {brand}\")\n", " self._brand = brand\n", " \n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Importing\n", "\n", "Let's say that we need access `Banana` class from banana.py file inside receipt.py. We can achive this by importing at the beginning of receipt.py:\n", "\n", "```python\n", "from food_store.product.fruit.banana import Banana\n", "\n", "# then it's used like this\n", "my_banana = Banana()\n", "```\n", "\n", "\n", "\n", "If we need to access multiple classes or functions from banana.py file:\n", "\n", "```python\n", "from food_store.product.fruit import banana\n", "\n", "# then it's used like this\n", "brands = banana.get_available_brands()\n", "my_banana = banana.Banana()\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A comprehensive introduction to modules and packages can be found [here](https://realpython.com/python-modules-packages/)." ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/notebooks/14_debugging.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Debugging with [`pdb`](https://docs.python.org/3/library/pdb.html#module-pdb)\n", "Your program does not always behave how you would expect it to behave. If the origin of the mistake is unclear, debugging is usually the most effective to find the root cause of the unexpected behavior. The Python Standard Library has a built-in debugger which is a powerful tool for solving any issues related to your code." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `breakpoint()`\n", "The basic use case for debugging is that you want to stop the execution of your program at some certain point and monitor variable values or program execution in general from that specific point onward. You stop the execution at the point you want by setting a breakpoint into code by `breakpoint()`.\n", "\n", "When you execute your program, the execution will stop at this point and will enter to interactive debugger session. You can add as many breakpoints into your code as you want." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Useful commands\n", "See the full list [here](https://docs.python.org/3/library/pdb.html#debugger-commands).\n", "\n", "* `h` or `help`: Prints a list of available commands. If you give an argument, e.g. `help continue`, prints help of the `continue` command.\n", "* `l` or `list`: List a piece of code around the current position.\n", "* `n` or `next`: Execute next line.\n", "* `s` or `step`: Same as `next` but \"steps into\" the function called in the next line.\n", "* `c` or `continue`: Continue execution until next breakpoint.\n", "* `r` or `return`: Continue execution until the return of current function.\n", "* `q` or `quit`: Quit debugger and abort program execution.\n", "\n", "Note that you can see the value of any variable by typing the variable name during the debugging session. You can also execute arbitrary code during the debugging session." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Let's see how it works\n", "Uncomment the `Pdb().set_trace()` (this is the Jupyter notebook equivalent for `breakpoint()`) lines and execute the cell. Execute the program line by line by using the commands defined above. Try all the above mentioned commands at least once. Pay attention to the difference between `n` and `s`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from IPython.core.debugger import Pdb\n", "\n", "\n", "class SuperGreeter:\n", " def __init__(self, people_to_greet):\n", " self.people = people_to_greet\n", "\n", " def greet(self):\n", " for person in self.people:\n", " if person.islower():\n", " self._greet_street_style(person)\n", " elif len(person) > 7:\n", " self._greet_hawaii(person)\n", " else:\n", " self._greet_polite(person)\n", "\n", " def _greet_polite(self, name):\n", " greeting = f\"G'day {name}! How are you doing?\"\n", " print(greeting)\n", "\n", " def _greet_street_style(self, name):\n", " # Pdb().set_trace() # UNCOMMENT\n", " name = name.upper()\n", " print(f\"WASSUP {name}!?\")\n", "\n", " def _greet_hawaii(self, name):\n", " print(f\"Aloha {name}!\")\n", "\n", "\n", "def main():\n", " people = [\"John Doe\", \"Donald\", \"Lisa\", \"alex\"]\n", " # Pdb().set_trace() # UNCOMMENT\n", " greeter = SuperGreeter(people)\n", " greeter.greet()\n", "\n", "\n", "main()" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/notebooks/15_std_lib.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Goodies of the [Python Standard Library](https://docs.python.org/3/library/#the-python-standard-library)\n", "The Python Standard Libary is part of your Python installation. It contains a wide range of packages which may be helpful while building your Python masterpieces. This notebook lists some of the commonly used packages and their main functionalities." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## [`datetime`](https://docs.python.org/3/library/datetime.html#module-datetime) for working with dates and times" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import datetime as dt\n", "\n", "local_now = dt.datetime.now()\n", "print(f\"local now: {local_now}\")\n", "\n", "utc_now = dt.datetime.utcnow()\n", "print(f\"utc now: {utc_now}\")\n", "\n", "# You can access any value separately:\n", "print(\n", " f\"{local_now.year} {local_now.month} {local_now.day} {local_now.hour} {local_now.minute} {local_now.second}\"\n", ")\n", "\n", "print(f\"date: {local_now.date()}\")\n", "print(f\"time: {local_now.time()}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `strftime()`\n", "For string formatting the `datetime`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "formatted1 = local_now.strftime(\"%Y/%m/%d-%H:%M:%S\")\n", "print(formatted1)\n", "\n", "formatted2 = local_now.strftime(\"date: %Y-%m-%d time:%H:%M:%S\")\n", "print(formatted2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `strptime()`\n", "For converting a datetime string into a `datetime` object " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_dt = dt.datetime.strptime(\"2000-01-01 10:00:00\", \"%Y-%m-%d %H:%M:%S\")\n", "print(f\"my_dt: {my_dt}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [`timedelta`](https://docs.python.org/3/library/datetime.html#timedelta-objects)\n", "For working with time difference." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "tomorrow = local_now + dt.timedelta(days=1)\n", "print(f\"tomorrow this time: {tomorrow}\")\n", "\n", "delta = tomorrow - local_now\n", "print(f\"tomorrow - now = {delta}\")\n", "print(f\"days: {delta.days}, seconds: {delta.seconds}\")\n", "print(f\"total seconds: {delta.total_seconds()}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Working with timezones" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import datetime as dt\n", "from zoneinfo import ZoneInfo\n", "\n", "naive_utc_now = dt.datetime.utcnow()\n", "print(f\"naive utc now: {naive_utc_now}, tzinfo: {naive_utc_now.tzinfo}\")\n", "\n", "# Localizing naive datetimes\n", "UTC_TZ = ZoneInfo(\"UTC\")\n", "utc_now = naive_utc_now.replace(tzinfo=UTC_TZ)\n", "print(f\"utc now: {utc_now}, tzinfo: {utc_now.tzinfo}\")\n", "\n", "# Converting localized datetimes to different timezone\n", "PARIS_TZ = ZoneInfo(\"Europe/Paris\")\n", "paris_now = utc_now.astimezone(PARIS_TZ)\n", "print(f\"Paris: {paris_now}, tzinfo: {paris_now.tzinfo}\")\n", "\n", "NEW_YORK_TZ = ZoneInfo(\"America/New_York\")\n", "ny_now = utc_now.astimezone(NEW_YORK_TZ)\n", "print(f\"New York: {ny_now}, tzinfo: {ny_now.tzinfo}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**NOTE**: If your project uses datetimes heavily, you may want to take a look at external libraries, such as [Pendulum](https://pendulum.eustace.io/docs/) and [Maya](https://github.com/kennethreitz/maya), which make working with datetimes easier for certain use cases." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## [`logging`](https://docs.python.org/3/library/logging.html#module-logging)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import logging\n", "\n", "# Handy way for getting a dedicated logger for every module separately\n", "logger = logging.getLogger(__name__)\n", "logger.setLevel(logging.WARNING)\n", "\n", "logger.debug(\"This is debug\")\n", "logger.info(\"This is info\")\n", "logger.warning(\"This is warning\")\n", "logger.error(\"This is error\")\n", "logger.critical(\"This is critical\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Logging expections\n", "There's a neat `exception` function in `logging` module which will automatically log the stack trace in addition to user defined log entry. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "try:\n", " path_calculation = 1 / 0\n", "except ZeroDivisionError:\n", " logging.exception(\"All went south in my calculation\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Formatting log entries" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import logging\n", "\n", "# This is only required for Jupyter notebook environment\n", "from importlib import reload\n", "\n", "reload(logging)\n", "\n", "my_format = \"%(asctime)s | %(name)-12s | %(levelname)-10s | %(message)s\"\n", "logging.basicConfig(format=my_format)\n", "\n", "logger = logging.getLogger(\"MyLogger\")\n", "\n", "logger.warning(\"Something bad is going to happen\")\n", "logger.error(\"Uups, it already happened\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Logging to a file" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import logging\n", "from pathlib import Path\n", "\n", "# This is only required for Jupyter notebook environment\n", "from importlib import reload\n", "\n", "reload(logging)\n", "\n", "logger = logging.getLogger(\"MyFileLogger\")\n", "\n", "# Let's define a file_handler for our logger\n", "log_path = Path.cwd() / \"my_log.txt\"\n", "file_handler = logging.FileHandler(log_path)\n", "\n", "# And a nice format\n", "formatter = logging.Formatter(\n", " \"%(asctime)s | %(name)-12s | %(levelname)-10s | %(message)s\"\n", ")\n", "file_handler.setFormatter(formatter)\n", "\n", "logger.addHandler(file_handler)\n", "\n", "# If you want to see it also in the console, add another handler for it\n", "# logger.addHandler(logging.StreamHandler())\n", "\n", "logger.warning(\"Oops something is going to happen\")\n", "logger.error(\"John Doe visits our place\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## [`random`](https://docs.python.org/3/library/random.html) for random number generation" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import random\n", "\n", "rand_int = random.randint(1, 100)\n", "print(f\"random integer between 1-100: {rand_int}\")\n", "\n", "rand = random.random()\n", "print(f\"random float between 0-1: {rand}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you need pseudo random numbers, you can set the `seed` for random. This will reproduce the output (try running the cell multiple times):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import random\n", "\n", "random.seed(5) # Setting the seed\n", "\n", "# Let's print 10 random numbers\n", "for _ in range(10):\n", " print(random.random())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## [`re`](https://docs.python.org/3/library/re.html#module-re) for regular expressions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Searching occurences" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import re\n", "\n", "secret_code = \"qwret 8sfg12f5 fd09f_df\"\n", "# \"r\" at the beginning means raw format, use it with regular expression patterns\n", "search_pattern = r\"(g12)\"\n", "\n", "match = re.search(search_pattern, secret_code)\n", "print(f\"match: {match}\")\n", "print(f\"match.group(): {match.group()}\")\n", "\n", "numbers_pattern = r\"[0-9]\"\n", "numbers_match = re.findall(numbers_pattern, secret_code)\n", "print(f\"numbers: {numbers_match}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Variable validation" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import re\n", "\n", "\n", "def validate_only_lower_case_letters(to_validate):\n", " pattern = r\"^[a-z]+$\"\n", " return bool(re.match(pattern, to_validate))\n", "\n", "\n", "print(validate_only_lower_case_letters(\"thisshouldbeok\"))\n", "print(validate_only_lower_case_letters(\"thisshould notbeok\"))\n", "print(validate_only_lower_case_letters(\"Thisshouldnotbeok\"))\n", "print(validate_only_lower_case_letters(\"thisshouldnotbeok1\"))\n", "print(validate_only_lower_case_letters(\"\"))" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/notebooks/16_testing2.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Testing with [pytest](https://docs.pytest.org/en/latest/) - part 2" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Let's make sure pytest and ipytest packages are installed\n", "# ipytest is required for running pytest inside Jupyter notebooks\n", "import sys\n", "\n", "!{sys.executable} -m pip install pytest\n", "!{sys.executable} -m pip install ipytest\n", "\n", "# These are needed for running pytest inside Jupyter notebooks\n", "import ipytest\n", "\n", "ipytest.autoconfig()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## [`@pytest.fixture`](https://docs.pytest.org/en/latest/fixture.html#pytest-fixtures-explicit-modular-scalable)\n", "Let's consider we have an implemention of `Person` class which we want to test." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class Person:\n", " def __init__(self, first_name, last_name, age):\n", " self.first_name = first_name\n", " self.last_name = last_name\n", " self.age = age\n", "\n", " @property\n", " def full_name(self):\n", " return f\"{self.first_name} {self.last_name}\"\n", "\n", " @property\n", " def as_dict(self):\n", " return {\"name\": self.full_name, \"age\": self.age}\n", "\n", " def increase_age(self, years):\n", " if years < 0:\n", " raise ValueError(\"Can not make people younger :(\")\n", " self.age += years" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can easily create resusable testing code by using pytest fixtures. If you introduce your fixtures inside [_conftest.py_](https://docs.pytest.org/en/latest/fixture.html#conftest-py-sharing-fixture-functions), the fixtures are available for all your test cases. In general, the location of _conftest.py_ is at the root of your _tests_ directory." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import pytest\n", "\n", "\n", "@pytest.fixture()\n", "def default_person():\n", " person = Person(first_name=\"John\", last_name=\"Doe\", age=82)\n", " return person" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then you can utilize `default_person` fixture in the actual test cases. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%ipytest\n", "\n", "\n", "def test_full_name(default_person): # Note: we use fixture as an argument of the test case\n", " result = default_person.full_name\n", " assert result == 'John Doe'\n", " \n", " \n", "def test_as_dict(default_person):\n", " expected = {'name': 'John Doe', 'age': 82}\n", " result = default_person.as_dict\n", " assert result == expected\n", " \n", " \n", "def test_increase_age(default_person):\n", " default_person.increase_age(1)\n", " assert default_person.age == 83\n", " \n", " default_person.increase_age(10)\n", " assert default_person.age == 93\n", " \n", " \n", "def test_increase_age_with_negative_number(default_person):\n", " with pytest.raises(ValueError):\n", " default_person.increase_age(-1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By using a fixture, we could use the same `default_person` for all our four test cases!\n", "\n", "In the `test_increase_age_with_negative_number` we used [`pytest.raises`](https://docs.pytest.org/en/latest/assert.html#assertions-about-expected-exceptions) to verify that an exception is raised. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## [`@pytest.mark.parametrize`](https://docs.pytest.org/en/latest/parametrize.html#pytest-mark-parametrize-parametrizing-test-functions)\n", "Sometimes you want to test the same functionality with multiple different inputs. `pytest.mark.parametrize` is your solution for defining multiple different inputs with expected outputs. Let's consider the following implementation of `replace_names` function. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def replace_names(original_str, new_name):\n", " \"\"\"Replaces names (uppercase words) of original_str by new_name\"\"\"\n", " words = original_str.split()\n", " manipulated_words = [new_name if w.istitle() else w for w in words]\n", " return \" \".join(manipulated_words)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can test the `replace_names` function with multiple inputs by using `pytest.mark.parametrize`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%ipytest\n", "\n", "\n", "@pytest.mark.parametrize(\"original,new_name,expected\", [\n", " ('this is Lisa', 'John Doe', 'this is John Doe'),\n", " ('how about Frank and Amy', 'John', 'how about John and John'),\n", " ('no names here', 'John Doe', 'no names here'),\n", " ])\n", "def test_replace_names(original, new_name, expected):\n", " result = replace_names(original, new_name)\n", " assert result == expected" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/notebooks/17_venv.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Virtual environment\n", "When working with Python projects, best practice is to have a separate virtual environment for each of them. \n", "\n", "Each virtual environment has its own Python binary. When you install some Python package into your virtual environment, it'll be installed only into that specific environment. This means that you can have different versions of a single Python package in different virtual environments in the same machine. Virtual environments are also useful if you need to use different Python versions in your projects." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## [`venv`](https://docs.python.org/3/library/venv.html#module-venv)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Creating new virtual environment\n", "You can create all your virtual environments into a single directory (for example, `.virtualenvs` directory inside your home folder). This makes them easier to find." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`python3 -m venv /path/to/new/environment`\n", "\n", "or\n", "\n", "`path/to/your/python -m venv /path/to/new/environment`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Activating the virtual environment" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Windows: `path_to_virtual_env\\Scripts\\activate.bat`
\n", "Posix: `source path_to_virtual_env/bin/activate`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Installing packages\n", "After activating the newly created virtual environment, you can install new packages by using `pip`. For example if you want to install `pytest`:\n", "\n", "`python -m pip install pytest`\n", "\n", "it'll be installed into path_to_virtual_env/lib//site-packages. Note that the path to site-packages maybe slightly different depending on the operating system you are using.\n", "\n", "You can list the installed packages and their versions by running:\n", "\n", "`python -m pip freeze`" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.5.4" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/beginner/notebooks/18_project_structure.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Project structure" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Python script\n", "Python is a great language for building small helper tools for various different kinds of tasks. Such small tools can be often expressed as a single file Python script.\n", "\n", "Here is an example structure for a Python script (aka executable Python module)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# the content of my_script.py\n", "\n", "# imports\n", "import logging\n", "\n", "# constants\n", "LOGGER = logging.getLogger()\n", "\n", "\n", "def magical_function():\n", " LOGGER.warning(\"We are about to do some magical stuff\")\n", "\n", "\n", "def main():\n", " # The actual logic of the script\n", " magical_function()\n", "\n", "\n", "if __name__ == \"__main__\":\n", " main()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Python package\n", "An example structure for a python project:\n", "\n", "```\n", "my_project/\n", " README.md\n", " requirements.txt\n", " setup.py\n", " \n", " src/\n", " my_project/\n", " __init__.py\n", " my_module.py\n", " other_module.py\n", " \n", " my_pkg1/\n", " __init__.py\n", " my_third_module.py\n", " \n", " tests/\n", " conftest.py\n", " test_module.py\n", " test_other_module.py\n", " \n", " my_pkg1/\n", " test_my_third_module.py\n", "\n", "```\n", "\n", "* [requirements.txt](https://pip.pypa.io/en/latest/user_guide/#requirements-files) lists the Python packages from which my_project depends on.\n", " * these can be installed by running `pip install -r requirements`\n", "* [setup.py](https://packaging.python.org/tutorials/distributing-packages/#setup-py) is a file in which you include relevant information about your project and the file is also used for packaging your project. Here's a minimal example of a setup.py:\n", "\n", "```python\n", "'''Minimal setup.py file'''\n", "\n", "from setuptools import setup, find_packages\n", "\n", "setup(\n", " name='my_project',\n", " version='0.1',\n", " packages=find_packages(where=\"src\"),\n", " package_dir={\"\": \"src\"})\n", "```\n", "* Once you have the setup.py file in place, you can install your project in editable mode by running `pip install -e .` in the root directory of your project. In editable mode the installed version is updated when you make changes to the source code files." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.5.4" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/intermediate/data/empty.txt ================================================ ================================================ FILE: notebooks/intermediate/data/misc_data1.txt ================================================ 1 John Doe John 2 99 John was here 6.72 this 2 is 2 totally random 2.0 john ================================================ FILE: notebooks/intermediate/data/misc_data2.txt ================================================ 1 John Doe John 2 101 John Doe was dOe here 6.72 this 2 is 2 67 totally random 2.0 john doE ================================================ FILE: notebooks/intermediate/exercises/01_std_lib2_exercise.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 1. Let's mock things!\n", "Below you can see `get_wiki_article` function which is a very simple implementation for fetching an article from wikipedia. Your task is to mock it's implementation such that it's going to always return `'Python is cool!'`. However, note that you should be able to check which argument is given to `urlopen` when `get_wiki_article` is called.\n", "\n", "**Note**: `get_content_of_url` uses [`urrlib`](https://docs.python.org/3/library/urllib.html#module-urllib), which is part of the Standard Library, for creating a HTTP request. Usually it's preferable to use [`requests`](http://docs.python-requests.org/en/master/) library (not part of the Standard Library) for such operations. Actually, `requests` uses `urllib` under the hood so it's good to know what's happening when you start using `requests` - or maybe you have already used it." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "from urllib.request import urlopen\n", "\n", "\n", "def get_wiki_article(name):\n", " url = f\"https://en.wikipedia.org/wiki/{name}\"\n", " response = urlopen(url)\n", " content = str(response.read())\n", " return content" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Your implementation here" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's verify it works as expected." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "article = \"Python_(programming_language)\"\n", "res = get_wiki_article(article)\n", "assert \"Guido van Rossum\" not in res, \"Guido is still there!\"\n", "assert res == \"Python is cool!\"\n", "urlopen.assert_called_with(\n", " \"https://en.wikipedia.org/wiki/Python_(programming_language)\"\n", ")\n", "\n", "print(\"All good!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 2. The power of `collections` module" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2.1 Creating a namedtuple\n", "Create a namedtuple `Car` which has fields `price`, `mileage`, and `brand`. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Your implemenation here" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's test it." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "car1 = Car(25000, 2000, \"BMW\")\n", "assert car1.price == 25000\n", "assert car1.mileage == 2000\n", "assert car1.brand == \"BMW\"\n", "assert isinstance(car1, tuple)\n", "\n", "# Note that indexing works also!\n", "# This means that if you change a tuple into a namedtuple,\n", "# the change will be backwards compatible.\n", "assert car1[2] == \"BMW\"\n", "\n", "print(\"All good!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The power of namedtuples is their simplicity. If `Car` would have been implemented as a class, the implementation would have been notably longer. However, if you would need to be able to e.g. change the `mileage` or `price` during the lifetime of a `Car` instance, consider using `class` because `tuples` are immutable." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2.2 dict of dicts\n", "Implement a `name_mapping` function which takes a collection of names as argument. \n", "\n", "#### The specification for `name_mapping`\n", "* you can assume that all the elements in the names collection are strings\n", "* if the provided names collection is empty, returns an empty dict\n", "* returns a dictionary of dictionaries\n", " * outer dictionary should contain keys `vowel` and `consonant`\n", " * `vowel` and `consonant` keys should have dictionaries of names (keys) and their occurences (values) as values\n", " * names belong to either `vowel` or `consonant` based on their first letter\n", " * vowels are defined by the `VOWELS` constant\n", " * if there are only names starting with a vowel, `consonant` key should not be present in the return value (same applies vice versa)\n", "* see the tests below for complete examples \n", "\n", "Tip: `defaultdict` and `Counter` may be helpful here :)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "VOWELS = (\"a\", \"e\", \"i\", \"o\", \"u\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def name_mapping(names):\n", " # Your implementation here" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's verify that it works correctly!" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "names = (\"Alice\", \"John\", \"Lisa\", \"John\", \"Eric\", \"Waldo\", \"annie\", \"Alice\", \"John\")\n", "expected = {\n", " \"consonant\": {\"John\": 3, \"Waldo\": 1, \"Lisa\": 1},\n", " \"vowel\": {\"Alice\": 2, \"annie\": 1, \"Eric\": 1},\n", "}\n", "assert name_mapping(names) == expected\n", "print(\"First ok!\")\n", "\n", "only_consonants = (\"John\", \"Doe\", \"Doe\")\n", "expected2 = {\"consonant\": {\"John\": 1, \"Doe\": 2}}\n", "assert name_mapping(only_consonants) == expected2\n", "print(\"Second ok!\")\n", "\n", "assert name_mapping([]) == {}\n", "\n", "print(\"All ok!\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.5.4" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/intermediate/exercises/05_idiomatic_python_exercise.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 1. Let's make it more idiomatic" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Your task is to refactor the following report generation code to more idiomatic. The existing implementation was written by an unknown developer who did not know anything about the idioms of Python. Luckily the unkown developer documented the implementation decently and wrote some tests for it. \n", "\n", "### The specification of the report generation\n", "\n", "This file content:\n", "```\n", "something\n", "1\n", "7\n", "somEThing\n", "\n", "2\n", "wassup\n", "woop\n", "woop\n", "something\n", "WoOP\n", "```\n", "\n", "Should yield this report:\n", "```\n", "missing values: 1\n", "highest number: 7.0\n", "most common words: something, woop\n", "occurrences of most common: 3\n", "#####\n", "numbers: [1.0, 7.0, 2.0]\n", "words: ['something', 'something', 'wassup', 'woop', 'woop', 'something', 'woop']\n", "```\n", "\n", "Note:\n", "* all numbers of the input file should be presented as floats in the report\n", "* all words are presented as lowercased in the report\n", "* while calculating the most common words, the count should be done as case insensitive (in other words, `'WoOp'` should be considered the same word as `'woop'`)\n", "* if there are multiple different most common words, they should be presented in the format presented above\n", "* there are more examples in the tests\n", "\n", "Run the cell of the existing implementation and then run the tests to verify that it works correctly. Then make sure that you understand how the legacy implementation works. After that, start refactoring, a function by function. Good luck!" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "def get_report(path):\n", " \"\"\"\n", " Creates a report of the file specified as argument.\n", "\n", " :param path: path to file from which the report should be created (string)\n", " :return: the report (string)\n", " \"\"\"\n", " data = _read_file(path)\n", " missing_count = data[0]\n", " numbers = data[1]\n", " words = data[2]\n", " report = _make_report(missing_count, numbers, words)\n", " return report\n", "\n", "\n", "def _read_file(path):\n", " \"\"\"\n", " Reads and returns the data from the file specified as argument.\n", "\n", " :param path: path to the file to be read.\n", " :return: a tuple containing\n", " 1. the number of empty lines (int)\n", " 2. numeric values (list of floats)\n", " 3. non-numeric values (list of strings)\n", " \"\"\"\n", " data_file = open(path)\n", " lines = data_file.readlines()\n", " line_count = len(lines)\n", " idx = 0\n", " empty_lines = 0\n", " words = []\n", " numbers = []\n", " while idx < line_count:\n", " line = lines[idx]\n", " line = line.strip()\n", " if line == \"\":\n", " empty_lines = empty_lines + 1\n", " else:\n", " is_number = False\n", " try:\n", " number = float(line)\n", " is_number = True\n", " except Exception:\n", " pass\n", "\n", " if is_number:\n", " numbers.append(number)\n", " else:\n", " words.append(line)\n", " idx = idx + 1\n", " data_file.close()\n", "\n", " return empty_lines, numbers, words\n", "\n", "\n", "def _make_report(missing_values, numbers, words):\n", " \"\"\"\n", " Creates and a report based on data given as arguments.\n", "\n", " :param missing_values: number of empty lines (int)\n", " :param numbers: numeric values (list of floats)\n", " :param words: non numeric values (list of strings)\n", " :return: the generated report (string)\n", " \"\"\"\n", " max_value = _get_max_value(numbers)\n", " lower_case_words = _words_to_lowercase(words)\n", " most_common_info = _get_most_common_words(lower_case_words)\n", " most_common_words = most_common_info[0]\n", " most_common_count = most_common_info[1]\n", "\n", " most_common_str = \"\"\n", " for idx in range(len(most_common_words)):\n", " most_common_str += most_common_words[idx] + \", \"\n", " # remove the last comma and space\n", " most_common_str = most_common_str[0 : len(most_common_str) - 2]\n", "\n", " report = (\n", " \"missing values: {}\\n\"\n", " \"highest number: {}\\n\"\n", " \"most common words: {}\\n\"\n", " \"occurrences of most common: {}\\n\"\n", " \"#####\\n\"\n", " \"numbers: {}\\n\"\n", " \"words: {}\"\n", " ).format(\n", " missing_values,\n", " max_value,\n", " most_common_str,\n", " most_common_count,\n", " numbers,\n", " lower_case_words,\n", " )\n", "\n", " return report\n", "\n", "\n", "def _get_max_value(numbers):\n", " \"\"\"\n", " Returns the greatest value of the list given as argument.\n", "\n", " :param numbers: numbers (list of numeric values)\n", " :return: greatest value of numbers, None if numbers is an empty list\n", " \"\"\"\n", " max_value = None\n", " if len(numbers) > 0:\n", " max_value = numbers[0]\n", " for idx in range(len(numbers)):\n", " if numbers[idx] > max_value:\n", " max_value = numbers[idx]\n", " return max_value\n", "\n", "\n", "def _words_to_lowercase(words):\n", " \"\"\"\n", " :param words: words to be converted (list of strings)\n", " :return: lowercased words (list of strings)\n", " \"\"\"\n", " lowercased = []\n", " for idx in range(len(words)):\n", " value = words[idx].lower()\n", " lowercased.append(value)\n", " return lowercased\n", "\n", "\n", "def _get_most_common_words(words):\n", " \"\"\"\n", " Finds the most common words in a list of words.\n", " If there are multiple different words with the same amount of occurrences,\n", " they are all included in the return value sorted alphabetically.\n", " In addition to returning the most common words, the return value\n", " includes also the count of occurrences of the most common words.\n", "\n", " :param words: list of words (list of strings)\n", " :return: a tuple containing:\n", " 1. most common words (list of strings)\n", " 2. the count of occurrences of the most common words (int)\n", " \"\"\"\n", " word_counts = {}\n", " idx = 0\n", " while idx < len(words):\n", " value = words[idx]\n", " if value not in word_counts.keys():\n", " word_counts[value] = 1\n", " else:\n", " word_counts[value] += 1\n", " idx = idx + 1\n", "\n", " max_count = 0\n", " for value in word_counts.values():\n", " if value > max_count:\n", " max_count = value\n", "\n", " most_common_words = []\n", " for word in word_counts.keys():\n", " count = word_counts[word]\n", " if count == max_count:\n", " most_common_words.append(word)\n", "\n", " most_common_words = sorted(most_common_words)\n", "\n", " return most_common_words, max_count" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now it's time refactor the existing code to make it more idiomatic.\n", "\n", "It's desirable that you do the refactoring in small chunks. Consider using the following workflow:\n", "1. Copy-paste a single function from the above ugly implementation to the cell below\n", "2. Refactor the function\n", "3. Run the tests to verify that you did not break anything\n", "\n", "This way you can consider each function as a separate sub task." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Your beautiful refactored, idiomatic, pythonic solution here" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The tests are here. Run these often while refactoring!" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "editable": false }, "outputs": [], "source": [ "from pathlib import Path\n", "\n", "CURRENT_DIR = Path.cwd()\n", "DATA_DIR = CURRENT_DIR.parent / \"data\"\n", "\n", "DATA_FILE1 = DATA_DIR / \"misc_data1.txt\"\n", "DATA_FILE2 = DATA_DIR / \"misc_data2.txt\"\n", "DATA_FILE3 = DATA_DIR / \"empty.txt\"\n", "\n", "\n", "expected1 = \"\"\"missing values: 2\n", "highest number: 99.0\n", "most common words: john\n", "occurrences of most common: 4\n", "#####\n", "numbers: [1.0, 2.0, 99.0, 6.72, 2.0, 2.0, 2.0]\n", "words: ['john', 'doe', 'john', 'john', 'was', 'here', 'this', 'is', 'totally', 'random', 'john']\"\"\"\n", "\n", "expected2 = \"\"\"missing values: 3\n", "highest number: 101.0\n", "most common words: doe, john\n", "occurrences of most common: 4\n", "#####\n", "numbers: [1.0, 2.0, 101.0, 6.72, 2.0, 2.0, 67.0, 2.0]\n", "words: ['john', 'doe', 'john', 'john', 'doe', 'was', 'doe', 'here', 'this', 'is', 'totally', 'random', 'john', 'doe']\"\"\"\n", "\n", "expected3 = \"\"\"missing values: 0\n", "highest number: None\n", "most common words: \n", "occurrences of most common: 0\n", "#####\n", "numbers: []\n", "words: []\"\"\"\n", "\n", "assert get_report(DATA_FILE1) == expected1\n", "print(\"First one OK!\")\n", "\n", "assert get_report(DATA_FILE2) == expected2\n", "print(\"Second one OK!\")\n", "\n", "assert get_report(DATA_FILE3) == expected3\n", "print(\"All OK, woop woop!\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# If the tests are failing, you can debug here.\n", "\n", "report = get_report(DATA_FILE1)\n", "print(report)" ] } ], "metadata": { "celltoolbar": "Edit Metadata", "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/intermediate/html/01_best_practices.html ================================================ 01_best_practices ================================================ FILE: notebooks/intermediate/html/01_idiomatic_loops.html ================================================ 01_idiomatic_loops ================================================ FILE: notebooks/intermediate/html/01_pytest_fixtures.html ================================================ 01_pytest_fixtures ================================================ FILE: notebooks/intermediate/html/01_std_lib2.html ================================================ 01_std_lib2 ================================================ FILE: notebooks/intermediate/html/02_idiomatic_dicts.html ================================================ 02_idiomatic_dicts ================================================ FILE: notebooks/intermediate/html/03_idiomatic_misc1.html ================================================ 03_idiomatic_misc1 ================================================ FILE: notebooks/intermediate/html/04_idiomatic_misc2.html ================================================ 04_idiomatic_misc2 ================================================ FILE: notebooks/intermediate/notebooks/01_best_practices.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Best practices in development" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## One virtual environment per project\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Why\n", "* Isolation\n", "* Different projects have different dependency versions\n", "* You don't want to mess up the system Python" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Tooling\n", "`poetry` is currently the recommended tool for dependency management but [\"consider other tools such as pip when poetry does not meet your use case\"](https://packaging.python.org/guides/tool-recommendations/#application-dependency-management). If `poetry` doesn't fit your use case, I recommend using `virtualenvwrapper` for managing virtual environments and `pip` for package management. On the other hand, if you're working on only a couple of projects, built-in [`venv`](https://docs.python.org/3/library/venv.html) will do just fine.\n", "#### [`poetry`](https://python-poetry.org/docs/)\n", "* Basically combines `pip`, `virtualenv`, and packaging facilities under single CLI\n", "* [pyproject.toml](https://python-poetry.org/docs/pyproject/) which replaces the need for requirements.txt and requirements-dev.txt \n", "* poetry.lock which pins dependencies, this means deterministic builds" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### [`virtualenvwrapper`](https://virtualenvwrapper.readthedocs.io/en/latest/)\n", "* If you are using Windows command prompt: [`virtualenvwrapper-win`](https://pypi.org/project/virtualenvwrapper-win/)\n", "* Like the name suggests, a wrapper around [`virtualenv`](https://pypi.org/project/virtualenv/)\n", "* Eases the workflow for creating, deleting, and (de)activating your virtual environments" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### [`pyenv`](https://github.com/pyenv/pyenv)\n", "* Easily change global / per project Python version \n", "* Also a tool for installing different Python versions (also different runtimes available, e.g. [PyPy](https://pypy.org/))\n", "* Useful if you'll need to work with different Python versions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Test your code\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Why\n", "* No surprises (especially in production)\n", "* Make sure that everything works as expected\n", "* Make sure that old stuff works as expected after introducing new features (regression)\n", "* Tests give you confidence while refactoring\n", "* Good tests demonstrate the use cases of application, i.e. they also document the implementation \n", "* ..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Tooling\n", "#### [`pytest`](https://docs.pytest.org/en/latest/)\n", "There's [`unittest`](https://docs.python.org/3/library/unittest.html) module in Python Standard Library but the go-to test runner nowadays is definitely `pytest`.\n", "\n", "Some reasons to use `pytest`:\n", "* [`fixtures`](https://docs.pytest.org/en/latest/fixture.html#fixture) for writing reusable testing code\n", "* [`markers`](https://docs.pytest.org/en/latest/example/markers.html) for splitting tests to different groups (e.g. smoke, run only on CI machine, etc) or skipping tests in certain conditions\n", "* [Automatic test discovery](https://docs.pytest.org/en/latest/goodpractices.html#test-discovery)\n", "* [Configurability](https://docs.pytest.org/en/latest/customize.html)\n", "* Active development of plugins, to mention a few:\n", " * [`pytest-cov`](https://pytest-cov.readthedocs.io/en/latest/) for coverage reporting\n", " * [`pytest-xdist`](https://github.com/pytest-dev/pytest-xdist) for speeding up test suit run time with parallelization\n", " * see [complete list](https://github.com/pytest-dev) of plugins maintained by `pytest`\n", "* Ease of [writing own plugins](https://docs.pytest.org/en/latest/writing_plugins.html)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### [`tox`](https://tox.readthedocs.io/en/latest/)\n", "`tox` makes it simple to test your code against different Python interpreter/runtime and dependency versions. This is important when you're writing software which should support different Python versions, which is usually the case with library-like packages. On the other hand, if you're developing, say, a web application which will be deployed to a known target platform, testing against multiple different versions is usually not necessary. However, `tox` makes it also possible to configure, for example, linting as part of `tox` run. Thus, `tox` may simplify the development workflow significantly by wrapping multiple different operations under a single command." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Write high quality code\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Why\n", "* Easy to read\n", "* Better maintainability\n", "* Better quality == less bugs" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import this" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Tooling - code formatters\n", "[PEP8](https://www.python.org/dev/peps/pep-0008/?) (see also [\"for humans version\"](https://pep8.org/)) describes the style guidelines for Python code, you should follow them. Luckily, there are awesome tools that handle this for you while you focus on writing code, not formatting it." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### [`black`](https://black.readthedocs.io/en/stable/)\n", "* This is the go-to formatter of the Python community" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Tooling - linters\n", "Automatic code formatting is great but in addition to that, you should use static analyzer (linter) to detect potential pitfalls in your code." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### [`ruff`](https://beta.ruff.rs/docs/)\n", "* Most comprehensive linter. As a bonus, it's extremely fast." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Tooling - pre-commit\n", "#### [`pre-commit`](https://pre-commit.com/)\n", "Ideally, all project contributors should follow the best practices of the project, let it be e.g. respecting PEP8 or making sure there's no linting errors or security vulnerabilities in their change sets. However, as code formatters and linters are (mainly) local tools, this can not be guaranteed. `pre-commit` let's you configure (.pre-commit-config.yaml file) a set of hooks that will be run as pre actions to a commit/push. After a developer has called once `pre-commit install`, these hooks will be then automatically ran prior each commit/push.\n", "* Run formatting before commit\n", "* Fail the commit in case linting errors\n", "* Even exercise the test suite before the code ends up to remote (might be time consuming in most scenarios though)\n", "* Easy to configure [your own hooks](https://pre-commit.com/#new-hooks) \n", "* And use the [existing ones](https://github.com/pre-commit/pre-commit-hooks)\n", "* There's also [pre-push option](https://pre-commit.com/#pre-commit-during-push)\n", "* Written in Python but supports also other languages, such as Ruby, Go, and Rust\n", "* Less failed CI builds!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Structure your code and projects\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Why\n", "* Package and module structure gives an overview about the project\n", "* Modular design == better reusability" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### How\n", "Some general guidelines:\n", "* Don't put too much stuff into one module\n", "* Split project into packages\n", "* Be consistent with your naming conventions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A few words about structuring your projects. If you're developing, say, a relative big business application, it makes sense to separate some of the non-core business logic packages into a separate project and publish that as separate package. This way the \"main\" repository doesn't get bloated and it's more approachable for newcomers. Additionally, there's a change that you (or someone else) can easily reuse this \"separated\" package in the future, which is often the case e.g. for different kinds of utility functionalities. \n", "\n", "Let's take a practical example. If your team has two different applications which interact with the same third party, it's beneficial to implement a separate client library for communication with it. This way a change is needed only in one place (in the client library) if the third party decides to make a backwards incompatible change in their API. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Tooling\n", "#### [`cookiecutter`](https://cookiecutter.readthedocs.io/en/latest/)\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cookiecutter is a tool which let's you create projects from predefined templates. \n", "\n", "* Rapid set-up of new projects, no need to copy paste from existing ones\n", "* Consistent development practices across all projects (project structure as well as e.g. `pre-commit`, `tox`, and CI configuration)\n", "* You can create one yourself or use some of the [existing ones](https://cookiecutter.readthedocs.io/en/latest/readme.html#python)\n", "* Written in Python but is applicable for non-Python projects as well, even non-programming related directory and file structures" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Use continuous integration and deployment\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "CI & CD belong to the best practices of software development without controversy, no matter what is the technology stack used for development. From Python point of view, CI is the place where we want to make sure that the other best practices described above are followed. For example, in bigger projects, it may not be even practical/possible to run the full test suite on developer's machine." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Why\n", "* Make sure the tests pass\n", "* CI is the place where it's possible to run also some time consuming tests which the impatient developers prefer to skip on their local machines\n", "* Make sure there's no linting errors\n", "* Ideally, the place to test against all target versions and platforms\n", "* Overall, CI is the last resort for automatically ensuring the quality \n", "* Manual deployments are time consuming and error-prone, CD is automated and deterministic\n", "* You want to automate as much as possible, human time is expensive\n", "* Minimize the time required for code reviews - what could be detected with automatic tools, should be detected by using those tools. Human time is expensive. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Tooling\n", "Tooling depends on which git repository manager option you've chosen and what kind of requirements you have. For example:\n", "* [GitHub Actions](https://github.com/features/actions)\n", "* If you're using Gitlab, it also has [integrated CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/)\n", "* Same for [BitBucket](https://www.atlassian.com/continuous-delivery/continuous-integration-tutorial)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Utilize the capabitilies of your editor" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Why\n", "* Efficient and fluent development\n", "* There's plenty of tools to make your daily programming easier, why would you not use them" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Tooling\n", "As there's a number of different editors and IDEs available, not to mention that everyone has their own preferences, I'll just focus on highlighting some of the features of my favorite IDE, PyCharm, which I highly recommend for Python development.\n", "\n", "#### [PyCharm](https://www.jetbrains.com/help/pycharm/quick-start-guide.html)\n", "* Good integration with `pytest`, e.g. run single tests / test classes / test modules\n", "* Git integration (in case you don't like command line)\n", "* Easy to configure to use automatic formatting, e.g [`black`](https://github.com/ambv/black#pycharm)\n", "* Intuitive searching capabilities\n", "* Refactoring features\n", "* Debugger\n", "* Jupyter Notebook integration\n", "* Free community edition already contains all you need" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Use existing solutions\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Why\n", "* Python Standard Library is extensive - and stable!\n", "* There are over 150k packages in [PyPI](https://pypi.org/)\n", "* Someone has most likely solved the problem you're trying to solve\n", "* Spend 5 minutes doing a google research before starting to solve a new problem, e.g. [stackoverflow](https://stackoverflow.com/) is a magical place." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Learn how to debug efficiently\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Why\n", "* You won't write completely stable code anyway - impossible looking conditions will occur. \n", "* When something is not working as expected, there are plenty of tools out there to help you figure out what's going on. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Tooling - debuggers\n", "#### [`pdb`](https://docs.python.org/3/library/pdb.html)\n", "* Part of the Standard Library\n", "* Sufficient for most use cases" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### [`ipdb`](https://pypi.org/project/ipdb/)\n", "* Feature rich `pdb` with similar API" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### [`pdb++`](https://pypi.org/project/pdbpp/)\n", "* Drop-in replacement for `pdb` with additional features" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Tooling - profilers" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### [`memray`](https://bloomberg.github.io/memray/)\n", "* Probably the only memory profiler you'll ever need" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### [`py-spy`](https://github.com/benfred/py-spy)\n", "* Profile running Python program without the need for modifying the source code or restarting the target process\n", "* Potential tool for identifying problems of e.g. a web application in production " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Tooling - runtime error tracking\n", "These are especially useful with web applications as you'll get reports - and notifications - of runtime exceptions with full tracebacks and variable values. This information is often enough for identifying the root cause of the problem, which is a huge benefit considering the time required for implementing and deploying the fix." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### [Sentry](https://docs.sentry.io/?platform=python)\n", "* Complete stack traces with relevant variable (`locals()`) values\n", "* Browser and OS information of the client\n", "* Support for other languages as well" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Tooling - misc" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Use logging instead of prints\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* [`logging`](https://docs.python.org/3/library/logging.html) is part of the Standard Library\n", "* With logging you can redirect the output to a file\n", "* Logs are usually the first place to look at after an end user reports an issue\n", "* You can specify the runtime level - no need to remove the debug prints" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### General guidelines\n", "* If you're building applications, use the latest Python.\n", "* If you're building libraries, make sure they support also older Python versions. \n", "* Develop in branches. Even if you're the only person in the project, branching makes it possible to easily switch between different features / bug fixes.\n", "* If you're not developing alone, practice code reviews. It's one of the best ways to learn for both parties.\n", "* Document your master pieces" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/intermediate/notebooks/01_idiomatic_loops.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Idiomatic loops" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Looping in general" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "data = [\"John\", \"Doe\", \"was\", \"here\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't do it like this. While loops are actually really rarely needed." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "idx = 0\n", "while idx < len(data):\n", " print(data[idx])\n", " idx += 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't do like this either." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for idx in range(len(data)):\n", " print(data[idx])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Do it like this!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for item in data:\n", " print(item)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you need the index as well, you can use enumerate." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for idx, val in enumerate(data):\n", " print(f\"{idx}: {val}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Looping over a range of numbers" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't do this." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "i = 0\n", "while i < 6:\n", " print(i)\n", " i += 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't do this either." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for val in [0, 1, 2, 3, 4, 5]:\n", " print(val)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Do it like this!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for val in range(6):\n", " print(val)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Reversed looping" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "data = [\"first\", \"to\", \"last\", \"from\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is no good." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "i = len(data) - 1\n", "while i >= 0:\n", " print(data[i])\n", " i -= 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Do it like this!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for item in reversed(data):\n", " print(item)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Looping over __n__ collections simultaneously" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "collection1 = [\"a\", \"b\", \"c\"]\n", "collection2 = (10, 20, 30, 40, 50)\n", "collection3 = [\"John\", \"Doe\", True]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Oh boy, not like this." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "shortest = len(collection1)\n", "if len(collection2) < shortest:\n", " shortest = len(collection2)\n", "if len(collection3) < shortest:\n", " shortest = len(collection3)\n", "\n", "i = 0\n", "while i < shortest:\n", " print(collection1[i], collection2[i], collection3[i])\n", " i += 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is getting better but there's even a better way!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "shortest = min(len(collection1), len(collection2), len(collection3))\n", "for i in range(shortest):\n", " print(collection1[i], collection2[i], collection3[i])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Do it like this!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for first, second, third in zip(collection1, collection2, collection3):\n", " print(first, second, third)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also create a dict out of two collections!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_dict = dict(zip(collection1, collection2))\n", "print(my_dict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `for - else` - Checking for a match in a collection\n", "Let's say we want to verify a certain condition is met by at least one element in a collection. Let's consider the following relatively naive example where we want to verify that at least one item is \"python\" (case insensitive) in `data`. If not, we'll raise a ValueError." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "data = [1, 2, 3, \"This\", \"is\", \"just\", \"a\", \"random\", \"Python\", \"list\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't do it like this" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "found = False\n", "for val in data:\n", " if str(val).lower() == \"python\":\n", " found = True\n", " break\n", "if not found:\n", " raise ValueError(\"Nope, couldn't find.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Do it like this!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for val in data:\n", " if str(val).lower() == \"python\":\n", " break\n", "else:\n", " raise ValueError(\"Nope, couldn't find.\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.5.4" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/intermediate/notebooks/01_pytest_fixtures.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Efficient use of `pytest` fixtures" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Required boilerplate for using `pytest` inside notebooks." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Let's make sure pytest and ipytest packages are installed\n", "# ipytest is required for running pytest inside Jupyter notebooks\n", "import sys\n", "\n", "!{sys.executable} -m pip install pytest\n", "!{sys.executable} -m pip install ipytest\n", "\n", "# These are needed for running pytest inside Jupyter notebooks\n", "import ipytest\n", "\n", "ipytest.autoconfig()\n", "\n", "import pytest" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Parametrizing fixtures\n", "Similarly as you can parametrize test functions with `pytest.mark.parametrize`, you can parametrize fixtures:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "PATHS = [\"/foo/bar.txt\", \"/bar/baz.txt\"]\n", "\n", "\n", "@pytest.fixture(params=PATHS)\n", "def executable(request):\n", " return request.param" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%ipytest -s\n", "\n", "def test_something_with_executable(executable):\n", " print(executable)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## [`pytest.mark.usefixtures`](https://docs.pytest.org/en/latest/fixture.html#usefixtures)\n", "[`pytest.mark.usefixtures`](https://docs.pytest.org/en/latest/fixture.html#usefixtures) is useful especially when you want to use some fixture in a set of tests but you don't need the return value of the fixture." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%ipytest -s\n", "\n", "\n", "@pytest.fixture\n", "def my_fixture():\n", " print(\"\\nmy_fixture is used\")\n", " \n", "\n", "@pytest.fixture\n", "def other_fixture():\n", " return \"FOO\"\n", "\n", "\n", "@pytest.mark.usefixtures('my_fixture')\n", "class TestMyStuff:\n", " def test_somestuff(self):\n", " pass\n", " \n", " def test_some_other_stuff(self, other_fixture):\n", " print(f'here we use also other_fixture which returns: {other_fixture}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `pytest` [built-in fixtures](https://docs.pytest.org/en/latest/builtin.html#pytest-api-and-builtin-fixtures)\n", "Here are a couple of examples of the useful built-in fixtures, you can view all available fixtures by running `pytest --fixtures`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [`monkeypatch`](https://docs.pytest.org/en/latest/reference.html#_pytest.monkeypatch.MonkeyPatch)\n", "Built-in [`monkeypatch`](https://docs.pytest.org/en/latest/reference.html#_pytest.monkeypatch.MonkeyPatch) fixture lets you e.g. set environment variables and set/delete attributes of objects. The use cases are similar as with patching/mocking with `unittest.mock.patch`/`unittest.mock.MagicMock` which are part of the Python Standard Library.\n", "\n", "**Monkeypatching environment variables:**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import os\n", "\n", "\n", "def get_env_var_or_none(var_name):\n", " return os.environ.get(var_name, None)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%ipytest -s\n", "\n", "def test_get_env_var_or_none_with_valid_env_var(monkeypatch):\n", " monkeypatch.setenv('MY_ENV_VAR', 'secret')\n", " res = get_env_var_or_none('MY_ENV_VAR')\n", " assert res == 'secret'\n", " \n", "def test_get_env_var_or_none_with_missing_env_var():\n", " res = get_env_var_or_none('NOT_EXISTING')\n", " assert res is None" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Monkeypatching attributes:**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class SomeClass:\n", " some_value = \"some value\"\n", "\n", " @staticmethod\n", " def tell_the_truth():\n", " print(\"This is the original truth\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def fake_truth():\n", " print(\"This is modified truth\")\n", "\n", "\n", "@pytest.fixture\n", "def fake_some_class(monkeypatch):\n", " monkeypatch.setattr(\"__main__.SomeClass.some_value\", \"fake value\")\n", " monkeypatch.setattr(\"__main__.SomeClass.tell_the_truth\", fake_truth)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%ipytest -s\n", "\n", "def test_some_class(fake_some_class):\n", " print(SomeClass.some_value)\n", " SomeClass.tell_the_truth()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [`tmpdir`](https://docs.pytest.org/en/latest/tmpdir.html#the-tmpdir-fixture)\n", "[`tmpdir`](https://docs.pytest.org/en/latest/tmpdir.html#the-tmpdir-fixture) fixture provides functionality for creating temporary files and directories." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def word_count_of_txt_file(file_path):\n", " with open(file_path) as f:\n", " content = f.read()\n", " return len(content.split())" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%ipytest -s\n", "\n", "def test_word_count(tmpdir):\n", " test_file = tmpdir.join('test.txt')\n", " test_file.write('This is example content of seven words')\n", " res = word_count_of_txt_file(str(test_file)) # str returns the path\n", " assert res == 7" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Fixture scope" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "@pytest.fixture(scope=\"function\")\n", "def func_fixture():\n", " print(\"\\nfunc\")\n", "\n", "\n", "@pytest.fixture(scope=\"module\")\n", "def module_fixture():\n", " print(\"\\nmodule\")\n", "\n", "\n", "@pytest.fixture(scope=\"session\")\n", "def session_fixture():\n", " print(\"\\nsession\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%ipytest -s\n", "\n", "def test_something(func_fixture, module_fixture, session_fixture):\n", " pass\n", "\n", "def test_something_else(func_fixture, module_fixture, session_fixture):\n", " pass" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Setup-teardown behaviour" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "@pytest.fixture\n", "def some_fixture():\n", " print(\"some_fixture is run now\")\n", " yield \"some magical value\"\n", " print(\"\\nthis will be run after test execution, you can do e.g. some clean up here\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%ipytest -s\n", "\n", "def test_something(some_fixture):\n", " print('running test_something')\n", " assert some_fixture == 'some magical value'\n", " print('test ends here')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Using fixtures automatically" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "@pytest.fixture(autouse=True)\n", "def my_fixture():\n", " print(\"\\nusing my_fixture\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%ipytest -s\n", "\n", "def test_1():\n", " pass\n", " \n", "def test_2():\n", " pass" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/intermediate/notebooks/01_std_lib2.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Goodies of the [Python Standard Library](https://docs.python.org/3/library/#the-python-standard-library)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## [`json`](https://docs.python.org/3/library/json.html#module-json) for encoding and decoding JSON\n", "Because the web is filled with JSON nowadays and the good days of xml are gone." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "data = {\"b\": True, \"a\": 1, \"nested\": {\"foo\": \"bar\"}, \"c\": None, \"some_list\": [1, 2, 3]}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Encoding" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import json\n", "\n", "json_data = json.dumps(data)\n", "print(f\"type: {type(json_data)} data: {json_data}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Decoding" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "decoded = json.loads(json_data)\n", "print(f\"type: {type(decoded)} data: {decoded}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## [`unittest.mock`](https://docs.python.org/3/library/unittest.mock.html#module-unittest.mock)\n", "Although `pytest` is the preferred testing framework, `unittest.mock` module offers some goodies that are helpful also in pytest test cases. Mocking and patching are generally useful for \"faking\" some parts of the logic/state of the software under test. Common use cases are, for example, patching parts of code that interact with 3rd parties (e.g. some web services)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [`MagicMock`](https://docs.python.org/3/library/unittest.mock.html#unittest.mock.MagicMock)\n", "\n", "In general, [Mocks](https://en.wikipedia.org/wiki/Mock_object) are simulated objects that replace the functionality/state of a real world object in a controlled way. Thus, they are especially useful in tests for mimicing some behavior of a specific part of the implementation under test.\n", "\n", "There is also a [`Mock`](https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock) class in the Python Standard Library but you usually want to use [`MagicMock`](https://docs.python.org/3/library/unittest.mock.html#unittest.mock.MagicMock) which is a subclass of `Mock`. `MagicMock` provides default implementation for the most of the magic methods (e.g. `__setitem__()` and `__getitem__()`)\n", "\n", "A potential use case could be something like this:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import random\n", "\n", "\n", "class Client:\n", " def __init__(self, url, username, password):\n", " self.url = url\n", " self.creds = (username, password)\n", "\n", " def fetch_some_data(self):\n", " print(\n", " \"Here we could for example fetch data from 3rd party API and return the data.\"\n", " )\n", " print(\"Now we will just return some random number between 1-100.\")\n", " return random.randint(1, 100)\n", "\n", "\n", "class MyApplication:\n", " def __init__(self):\n", " self.client = Client(\n", " url=\"https://somewhere/api\", username=\"John Doe\", password=\"secret123?\"\n", " )\n", "\n", " def do_something_fancy(self):\n", " data = self.client.fetch_some_data()\n", " return data ** (1 / 2) # let's return a square root just for example\n", "\n", "\n", "####################\n", "# In the test module:\n", "\n", "from unittest.mock import MagicMock\n", "\n", "# Inside a test case:\n", "app = MyApplication()\n", "app.client = MagicMock() # Mock the client\n", "app.client.fetch_some_data.return_value = 4 # Set controlled behaviour\n", "result = app.do_something_fancy()\n", "assert result == 2\n", "print(\"All good, woop woop!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [`patch`](https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch)\n", "The use cases of [`patch`](https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch) are pretty similar to `MacigMock`. The biggest difference is that `patch` is used as a context manager or a decorator. Object to be patched is given as an argument for `patch`. In addition, you can provide additional object as a second argument (`new`) which will replace the original one. In case the `new` is omitted, `MagicMock` will be used by default.\n", "\n", "Let's see how the example above would look like with `patch`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# In the test module:\n", "\n", "from unittest.mock import patch\n", "\n", "# Inside a test case:\n", "app = MyApplication()\n", "with patch(\"__main__.app.client\") as patched_client: # Patch the client\n", " patched_client.fetch_some_data.return_value = 4 # Set controlled behaviour\n", " result = app.do_something_fancy()\n", " assert result == 2\n", " print(\"All good, woop woop!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The same but with a function decorator instead of a context manager. Note that here we are patching the whole `Client` class, not just the `client` instance variable of `app`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from unittest.mock import patch\n", "\n", "\n", "@patch(\"__main__.Client\") # Patch the Client\n", "def test_do_something_fancy(client_cls):\n", " client_cls().fetch_some_data.return_value = 4 # Set controlled behaviour\n", " app = MyApplication()\n", " result = app.do_something_fancy()\n", " assert result == 2\n", " print(\"All good, woop woop!\")\n", "\n", "\n", "test_do_something_fancy() # This is just for the sake of example" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## [`collections`](https://docs.python.org/3/library/collections.html#module-collections)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [`namedtuple`](https://docs.python.org/3/library/collections.html#collections.namedtuple)\n", "A great helper for creating more readable and self documenting code.\n", "\n", "`namedtuple` is a function that returns a tuple whose fields have names and also the tuple itself has a name (just like classes and their instance variables). Potential use cases include storing data which should be immutable. If you can use Python 3.7 or newer, you may want to take a look at [`dataclasses`](https://docs.python.org/3/library/dataclasses.html#module-dataclasses) as well. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from collections import namedtuple\n", "\n", "Person = namedtuple(\"Person\", [\"name\", \"age\", \"is_gangster\"])\n", "\n", "# instance creation is similar to classes\n", "john = Person(\"John Doe\", 83, True)\n", "lisa = Person(\"Lis Doe\", age=77, is_gangster=False)\n", "\n", "print(john, lisa)\n", "print(f\"Is John a gangster: {john.is_gangster}\")\n", "\n", "# tuples are immutable, thus you can't do this\n", "# john.is_gangster = False" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [`Counter`](https://docs.python.org/3/library/collections.html#collections.Counter)\n", "For counting the occurences of elements in a collection." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from collections import Counter\n", "\n", "data = [1, 2, 3, 1, 2, 4, 5, 6, 2]\n", "\n", "counter = Counter(data)\n", "print(f\"type: {type(counter)}, counter: {counter}\")\n", "\n", "print(f\"count of twos: {counter[2]}\")\n", "print(f\"count of tens: {counter[10]}\") # zero for non existing\n", "\n", "print(f\"counter is a dict: {isinstance(counter, dict)}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [`defaultdict`](https://docs.python.org/3/library/collections.html#collections.defaultdict)\n", "For cleaner code for populating dictionaries.\n", "\n", "Let's first see how you could use a normal `dict`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "data = (1, 2, 3, 4, 3, 2, 5, 6, 7)\n", "\n", "my_dict = {}\n", "for val in data:\n", " if val % 2:\n", " if not \"odd\" in my_dict:\n", " my_dict[\"odd\"] = []\n", " my_dict[\"odd\"].append(val)\n", " else:\n", " if not \"even\" in my_dict:\n", " my_dict[\"even\"] = []\n", " my_dict[\"even\"].append(val)\n", "\n", "print(my_dict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With `defaultdict`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from collections import defaultdict\n", "\n", "my_dict = defaultdict(list)\n", "for val in data:\n", " if val % 2:\n", " my_dict[\"odd\"].append(val)\n", " else:\n", " my_dict[\"even\"].append(val)\n", "print(my_dict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the above example, `defaultdict` makes sure that a fresh `list` is automatically initialized as a value when a new key is added.\n", "\n", "Here's another example with `int` as a default." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_dict = defaultdict(int)\n", "for val in data:\n", " if val % 2:\n", " my_dict[\"odd_count\"] += 1\n", " else:\n", " my_dict[\"even_count\"] += 1\n", "print(my_dict)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.5.4" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/intermediate/notebooks/02_idiomatic_dicts.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Idiomatic dictionaries" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `get` - default value of a non existing key while accessing\n", "Especially handy if you're unsure about the presence of a key." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_dict = {\"a\": 1, \"b\": 2, \"c\": 3}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't do it like this." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "if \"g\" in my_dict:\n", " value = my_dict[\"g\"]\n", "else:\n", " value = \"some default value\"\n", "print(value)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or like this." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "try:\n", " value = my_dict[\"g\"]\n", "except KeyError:\n", " value = \"some default value\"\n", "print(value)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Do it like this!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "value = my_dict.get(\"g\", \"some default value\")\n", "print(value)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that if you don't provide the default value for `get`, the return value will be `None` if the key is not present in the dictionary" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "value = my_dict.get(\"g\")\n", "print(value is None)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `setdefault` - same as `get` but also sets the value if not present" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't do it like this." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_dict = {\"a\": 1, \"b\": 2, \"c\": 3}\n", "\n", "key = \"g\"\n", "if key in my_dict:\n", " value = my_dict[key]\n", "else:\n", " value = \"some default value\"\n", " my_dict[key] = value\n", "\n", "print(value)\n", "print(my_dict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Let's do it like this!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_dict = {\"a\": 1, \"b\": 2, \"c\": 3}\n", "\n", "key = \"g\"\n", "value = my_dict.setdefault(key, \"some default value\")\n", "\n", "print(value)\n", "print(my_dict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Comprehensions\n", "Let's say we have a collection of numbers and we want to store those as a dictionary where the number is key and it's square is the value." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "numbers = (1, 5, 10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't do it like this." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "squares = {}\n", "for num in numbers:\n", " squares[num] = num**2\n", "print(squares)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Do it like this!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "squares = {num: num**2 for num in numbers}\n", "print(squares)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Another example" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "keys = (\"a\", \"b\", \"c\")\n", "values = [True, 100, \"John Doe\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't do it like this." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_dict = {}\n", "for idx, key in enumerate(keys):\n", " my_dict[key] = values[idx]\n", "print(my_dict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Do it like this!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_dict = {k: v for k, v in zip(keys, values)}\n", "print(my_dict)\n", "\n", "# Or even like this:\n", "my_dict2 = dict(zip(keys, values))\n", "\n", "assert my_dict2 == my_dict" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Looping" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_dict = {\"age\": 83, \"is gangster\": True, \"name\": \"John Doe\"}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't do it like this." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for key in my_dict:\n", " val = my_dict[key]\n", " print(f\"key: {key:15s} value: {val}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Do it like this!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for key, val in my_dict.items():\n", " print(f\"key: {key:15s} value: {val}\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.5.4" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/intermediate/notebooks/03_idiomatic_misc1.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Idiomatic Python - miscellaneous part 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Comprehensions" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "original_data = (1, 2, 3, 4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't do this." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# list\n", "square_roots_list = []\n", "for val in original_data:\n", " square_root = val ** (1 / 2)\n", " square_roots_list.append(square_root)\n", "print(square_roots_list)\n", "\n", "# set\n", "square_roots_set = set()\n", "for val in original_data:\n", " square_root = val ** (1 / 2)\n", " square_roots_set.add(square_root)\n", "print(square_roots_set)\n", "\n", "# dict\n", "square_roots_dict = {}\n", "for val in original_data:\n", " square_root = val ** (1 / 2)\n", " square_roots_dict[val] = square_root\n", "print(square_roots_dict)\n", "\n", "# dict with a condition\n", "integer_square_roots_dict = {}\n", "for val in original_data:\n", " square_root = val ** (1 / 2)\n", " if square_root.is_integer():\n", " integer_square_roots_dict[val] = square_root\n", "print(integer_square_roots_dict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note: in case you're using 2.X version of Python for some reason, the result of `1/2` is `0` instead of `0.5`. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Use comprehensions!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "square_roots_list = [val ** (1 / 2) for val in original_data]\n", "print(square_roots_list)\n", "\n", "square_roots_set = {val ** (1 / 2) for val in original_data}\n", "print(square_roots_set)\n", "\n", "square_roots_dict = {val: val ** (1 / 2) for val in original_data}\n", "print(square_roots_dict)\n", "\n", "integer_square_roots_dict = {\n", " val: val ** (1 / 2) for val in original_data if (val ** (1 / 2)).is_integer()\n", "}\n", "print(integer_square_roots_dict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Using `in` for checking presence of an element in a collection" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "name = \"John Doe\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't do it like this." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "if name == \"John\" or name == \"Doe\" or name == \"John Doe\":\n", " print(\"This seems to be our guy\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Do it like this!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "if name in (\"John\", \"Doe\", \"John Doe\"):\n", " print(\"This seems to be our guy\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Chained comparisons" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a, b, c, d = 1, 2, 3, 4" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't do it like this." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "if b > a and c > b and d > c:\n", " print(\"from lowest to highest: a, b, c, d\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Do it like this!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "if a < b < c < d:\n", " print(\"from lowest to highest: a, b, c, d\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Falsy/truthy values" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# These are falsy\n", "my_list = []\n", "my_dict = {}\n", "my_set = set()\n", "my_tuple = tuple()\n", "zero = 0\n", "false = False\n", "none = None\n", "my_str = \"\"\n", "\n", "# Basically the rest are truthy\n", "# for example:\n", "my_second_list = [\"foo\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't do it like this." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "if len(my_list) == 0:\n", " print(\"Empty list is so empty\")\n", "\n", "if not len(my_dict):\n", " print(\"Empty dict is also very empty\")\n", "\n", "if not len(my_set) and not len(my_tuple):\n", " print(\"Same goes for sets and tuples\")\n", "\n", "if not bool(zero) and not bool(false) and not bool(none) and len(my_str) == 0:\n", " print(\"These are also falsy\")\n", "\n", "if len(my_second_list) > 0:\n", " print(\"This should be true\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### This is much better!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "if not my_list:\n", " print(\"Empty list is so empty\")\n", "\n", "if not my_dict:\n", " print(\"Empty dict is also very empty\")\n", "\n", "if not my_set and not my_tuple:\n", " print(\"Same goes for sets and tuples\")\n", "\n", "if not zero and not false and not none and not my_str:\n", " print(\"These are also falsy\")\n", "\n", "if my_second_list:\n", " print(\"This should be true\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `any` & `all`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "example_collection = [\"a\", True, \"Python is cool\", 123, 0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't do it like this." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "any_value_truthy = True\n", "for val in example_collection:\n", " if val:\n", " any_value_truthy = True\n", " break\n", "\n", "all_values_truthy = True\n", "for val in example_collection:\n", " if not val:\n", " all_values_truthy = False\n", " break\n", "\n", "print(f\"any truthy: {any_value_truthy}, all truthy: {all_values_truthy}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Do it like this!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "any_value_truthy = any(example_collection)\n", "all_values_truthy = all(example_collection)\n", "print(f\"any truthy: {any_value_truthy}, all truthy: {all_values_truthy}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Pythonic substitute for ternary operator\n", "Many other programming languages have a ternary operator: `?`. A common use case for the ternary operator is to assign a certain value to a variable based on some condition. In other words, it could be used like this:\n", "```\n", "variable = some_condition ? some_value : some_other_value\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Instead of doing this." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "some_condition = True # just a dummy condition\n", "\n", "if some_condition:\n", " variable = \"John\"\n", "else:\n", " variable = \"Doe\"\n", "print(variable)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### You can do it like this!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "variable = \"John\" if some_condition else \"Doe\"\n", "print(variable)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Function keywords arguments\n", "For better readability and maintainability." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def show_person_details(name, is_gangster, is_hacker, age):\n", " print(f\"name: {name}, gangster: {is_gangster}, hacker: {is_hacker}, age: {age}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is not good. It's hard to tell what `True`, `False` and `83` refer here if you are not familiar with the signature of the `show_person_details` function." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "show_person_details(\"John Doe\", True, False, 83)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### This is much better!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "show_person_details(\"John Doe\", is_gangster=True, is_hacker=False, age=83)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Extra: keyword only arguments after `*`\n", "This might be useful for example if the signature of the function is likely to change in the future. For example, if there's even a slight chance that one of the arguments may be dropped during the future development, consider using `*`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def func_with_loads_of_args(arg1, *, arg2=None, arg3=None, arg4=None, arg5=\"boom\"):\n", " pass\n", "\n", "\n", "# This won't work because only keyword arguments allowed after *\n", "# func_with_loads_of_args('John Doe', 1, 2)\n", "\n", "# This is ok\n", "func_with_loads_of_args(\"John Doe\", arg4=\"foo\", arg5=\"bar\", arg2=\"foo bar\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Multiple assigment\n", "Let's say we want to swap the values of two variables." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't do it like this." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# original values\n", "a = 1\n", "b = 2\n", "\n", "# swap\n", "tmp = a\n", "a = b\n", "b = tmp\n", "print(a, b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Do it like this!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# original values\n", "a = 1\n", "b = 2\n", "\n", "# swap\n", "a, b = b, a\n", "print(a, b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## (Un)packing" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_list = [1, 2, 3, 4, 5, 6]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't do something like this." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "first = my_list[0]\n", "last = my_list[-1]\n", "middle = my_list[1:-1]\n", "print(first, middle, last)\n", "\n", "packed = [first] + middle + [last]\n", "assert packed == my_list" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### This is the Pythonic way!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# unpacking\n", "first, *middle, last = my_list\n", "print(first, middle, last)\n", "\n", "# packing\n", "packed = [first, *middle, last]\n", "assert packed == my_list" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: notebooks/intermediate/notebooks/04_idiomatic_misc2.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Idiomatic Python - miscellaneous part 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## String concatenation" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "names = (\"John\", \"Lisa\", \"Terminator\", \"Python\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't do this." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "semicolon_separated = names[0]\n", "for name in names[1:]:\n", " semicolon_separated += \";\" + name\n", "print(semicolon_separated)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Use `join` instead!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "semicolon_separated = \";\".join(names)\n", "print(semicolon_separated)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `or` in assignments\n", "The return value of `a or b`:\n", "* `a` if `a` is truthy\n", "* `b` otherwise\n", "\n", "You can take advantage of this e.g. while writing variable assignments." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a = 0\n", "b = None\n", "c = \"John Doe\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Instead of doing something like this:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_variable = \"default value\"\n", "if a:\n", " my_variable = a\n", "elif b:\n", " my_variable = b\n", "elif c:\n", " my_variable = c\n", "print(my_variable)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Prefer doing this:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_variable = a or b or c or \"default value\"\n", "print(my_variable)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `try` - `except` - `else`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't use the following technique for checking if there was exceptions during execution of some block of code." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "exception_occured = False\n", "try:\n", " # here would be the logic of your master piece\n", "\n", " bad_calculation = 1 / 0\n", "\n", "except ValueError as e:\n", " print(f\"Oh boi, some value error: {e}\")\n", " exception_occured = True\n", "except Exception as e:\n", " print(f\"Oh boi, something bad happened: {e}\")\n", " exception_occured = True\n", "\n", "if not exception_occured:\n", " print(\"All went well!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Use this instead!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "try:\n", " # here would be the logic of your master piece\n", "\n", " bad_calculation = 1 / 0\n", "\n", "except ValueError as e:\n", " print(f\"Oh boi, some keyerror: {e}\")\n", "except Exception as e:\n", " print(f\"Oh boi, something bad happened: {e}\")\n", "else:\n", " print(\"All went well!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `try` - `finally`\n", "For scenarios where you want to do something always, even when there are exceptions." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't do it like this" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def magical_calculation():\n", " try:\n", " # here would be the logic of your master piece\n", " result = 1 / 0\n", " except ZeroDivisionError:\n", " print(\"This could be something important that should be done every time\")\n", " return 0\n", " except Exception:\n", " print(\"This could be something important that should be done every time\")\n", " return None\n", "\n", " print(\"This could be something important that should be done every time\")\n", " return result\n", "\n", "\n", "print(f\"return value: {magical_calculation()}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### This is better fit for the purpose!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def magical_calculation():\n", " try:\n", " # here would be the logic of your master piece\n", " result = 1 / 0\n", " except ZeroDivisionError:\n", " return 0\n", " except Exception:\n", " return None\n", " finally:\n", " print(\"This could be something important that should be done every time\")\n", " return result\n", "\n", "\n", "print(f\"return value: {magical_calculation()}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Note**: You can also have `try`-`except`-`else`-`finally` structure. In cases where exception is not raised inside `try`, `else` will be executed before `finally`. If there is an expection, `else` block is not executed." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Use context managers when possible\n", "One use case example is file I/O." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't play with files like this." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "try:\n", " some_file = open(\"tmp.txt\", \"w\")\n", " print(f\"the file is now open: {not some_file.closed}\")\n", "\n", " # here would be some logic\n", "\n", "finally:\n", " some_file.close()\n", " print(f\"now it's closed: {some_file.closed}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Use context manager instead!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "with open(\"tmp.txt\", \"w\") as some_file:\n", " print(f\"the file is now open: {not some_file.closed}\")\n", "\n", " # here would be some logic\n", "\n", "print(f\"now it's closed: {some_file.closed}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### It's also easy to implement one yourself." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from contextlib import contextmanager\n", "\n", "\n", "@contextmanager\n", "def my_context():\n", " print(\"Entering to my context\")\n", " yield\n", " print(\"Exiting my context\")\n", "\n", "\n", "def do_stuff():\n", " with my_context():\n", " print(\"Doing stuff\")\n", "\n", " print(\"Doing some stuff outside my context\")\n", "\n", "\n", "do_stuff()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `min()` & `max()`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "secret_data = (1, 2, 5, 99, 8, -9)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "No need to bake it yourself." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "max_value = 0\n", "for val in secret_data:\n", " if val > max_value:\n", " max_value = val\n", "print(max_value)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Use builtin functionality instead!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "max_value = max(secret_data)\n", "print(max_value)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `contextlib.suppress` - ignoring exceptions " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If there's a potential exception that is ok, don't handle it like this." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "value = 0\n", "try:\n", " value = 1 / 0 # just for demonstrating purposes\n", "except ZeroDivisionError:\n", " pass\n", "\n", "print(value)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Do it like this instead!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from contextlib import suppress\n", "\n", "value = 0\n", "with suppress(ZeroDivisionError):\n", " value = 1 / 0 # just for demonstrating purposes\n", "\n", "print(value)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Properties instead of getter/setter methods" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Instead of doing something like this." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class Person:\n", " def __init__(self, first_name, last_name):\n", " self.first_name = first_name\n", " self.last_name = last_name\n", "\n", " def get_full_name(self):\n", " return f\"{self.first_name} {self.last_name}\"\n", "\n", " def set_full_name(self, full_name):\n", " parts = full_name.split()\n", " if len(parts) != 2:\n", " raise ValueError(\"Sorry, too difficult name\")\n", "\n", " self.first_name, self.last_name = parts\n", "\n", "\n", "p = Person(\"John\", \"Doe\")\n", "print(p.get_full_name())\n", "p.set_full_name(\"Lisa Doe\")\n", "print(p.get_full_name())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Prefer properties!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class Person:\n", " def __init__(self, first_name, last_name):\n", " self.first_name = first_name\n", " self.last_name = last_name\n", "\n", " @property\n", " def full_name(self):\n", " return f\"{self.first_name} {self.last_name}\"\n", "\n", " @full_name.setter\n", " def full_name(self, name):\n", " parts = name.split()\n", " if len(parts) != 2:\n", " raise ValueError(\"Sorry, too difficult name\")\n", "\n", " self.first_name, self.last_name = parts\n", "\n", "\n", "p = Person(\"John\", \"Doe\")\n", "print(p.full_name)\n", "p.full_name = \"Lisa Doe\"\n", "print(p.full_name)" ] } ], "metadata": { "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.11.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: pytest.ini ================================================ [pytest] norecursedirs = exercises* .ipynb_checkpoints* ================================================ FILE: scripts/notebook_to_html.py ================================================ from pathlib import Path import subprocess import sys def main(): path = sys.argv[1] if path.strip().lower() == "all": convert_all_notebooks_to_html() else: convert_notebook_to_html(path) def convert_all_notebooks_to_html(): notebook_dir = Path(__file__).parent.parent / "notebooks" for directory in ( notebook_dir / "beginner" / "notebooks", notebook_dir / "intermediate" / "notebooks", ): for notebook_path in directory.glob("*.ipynb"): convert_notebook_to_html(notebook_path) def convert_notebook_to_html(notebook_path): path = Path(notebook_path) if not path.exists(): raise SystemExit(f"Invalid path {path}") output_dir = path.parent.parent / "html" cmd = f"jupyter nbconvert --to html --execute --ExecutePreprocessor.timeout=30 --output-dir {output_dir} {path.absolute()}" subprocess.check_call(cmd.split()) if __name__ == "__main__": main()