{ "cells": [ { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Python good practices" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Environment setup" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "skip" }, "tags": [ "hide-output" ] }, "outputs": [], "source": [ "!pip install papermill" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Python version: 3.7.5\n" ] } ], "source": [ "import platform\n", "\n", "print(f\"Python version: {platform.python_version()}\")\n", "assert platform.python_version_tuple() >= (\"3\", \"6\")\n", "\n", "import os\n", "import papermill as pm\n", "\n", "from IPython.display import YouTubeVideo" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Writing pythonic code" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The Zen of Python, by Tim Peters\n", "\n", "Beautiful is better than ugly.\n", "Explicit is better than implicit.\n", "Simple is better than complex.\n", "Complex is better than complicated.\n", "Flat is better than nested.\n", "Sparse is better than dense.\n", "Readability counts.\n", "Special cases aren't special enough to break the rules.\n", "Although practicality beats purity.\n", "Errors should never pass silently.\n", "Unless explicitly silenced.\n", "In the face of ambiguity, refuse the temptation to guess.\n", "There should be one-- and preferably only one --obvious way to do it.\n", "Although that way may not be obvious at first unless you're Dutch.\n", "Now is better than never.\n", "Although never is often better than *right* now.\n", "If the implementation is hard to explain, it's a bad idea.\n", "If the implementation is easy to explain, it may be a good idea.\n", "Namespaces are one honking great idea -- let's do more of those!\n" ] } ], "source": [ "import this" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### What does \"Pythonic\" mean?\n", "\n", "- Python code is considered _pythonic_ if it:\n", " - conforms to the Python philosophy;\n", " - takes advantage of the language's specific features.\n", "- Pythonic code is nothing more than **idiomatic Python code** that strives to be clean, concise and readable." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Example: swapping two variables" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "a = 3\n", "b = 2\n", "\n", "# Non-pythonic\n", "tmp = a\n", "a = b\n", "b = tmp\n", "\n", "# Pythonic\n", "a, b = b, a" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Example: iterating on a list" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "my_list = [\"a\", \"b\", \"c\"]\n", "\n", "\n", "def do_something(item):\n", " # print(item)\n", " pass\n", "\n", "\n", "# Non-pythonic\n", "i = 0\n", "while i < len(my_list):\n", " do_something(my_list[i])\n", " i += 1\n", "\n", "# Still non-pythonic\n", "for i in range(len(my_list)):\n", " do_something(my_list[i])\n", "\n", "# Pythonic\n", "for item in my_list:\n", " do_something(item)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Example: indexed traversal" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 -> a\n", "1 -> b\n", "2 -> c\n", "0 -> a\n", "1 -> b\n", "2 -> c\n" ] } ], "source": [ "my_list = [\"a\", \"b\", \"c\"]\n", "\n", "# Non-pythonic\n", "for i in range(len(my_list)):\n", " print(i, \"->\", my_list[i])\n", "\n", "# Pythonic\n", "for i, item in enumerate(my_list):\n", " print(i, \"->\", item)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Example: searching in a list" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "fruits = [\"apples\", \"oranges\", \"bananas\", \"grapes\"]\n", "fruit = \"cherries\"\n", "\n", "# Non-pythonic\n", "found = False\n", "size = len(fruits)\n", "for i in range(0, size):\n", " if fruits[i] == fruit:\n", " found = True\n", "\n", "# Pythonic\n", "found = fruit in fruits" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Example: generating a list\n", "\n", "This feature is called [list comprehension](https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions)." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "numbers = [1, 2, 3, 4, 5, 6]\n", "\n", "# Non-pythonic\n", "doubles = []\n", "for i in range(len(numbers)):\n", " if numbers[i] % 2 == 0:\n", " doubles.append(numbers[i] * 2)\n", " else:\n", " doubles.append(numbers[i])\n", "\n", "# Pythonic\n", "doubles = [x * 2 if x % 2 == 0 else x for x in numbers]" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Code style\n", "\n", "- [PEP8](https://www.python.org/dev/peps/pep-0008/) is the official style guide for Python:\n", " - use 4 spaces for indentation;\n", " - define a maximum value for line length (around 80 characters);\n", " - organize imports at beginning of file;\n", " - surround binary operators with a single space on each side;\n", " - ...\n", "- Code style should be enforced upon creation by a tool like [black](https://github.com/psf/black)." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Beyond PEP8\n", "\n", "Focusing on style and PEP8-compliance might make you miss more fundamental code imperfections." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "image/jpeg": "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EBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREH/9k=\n", "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "YouTubeVideo(\"wf-BqAjZb8M\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Docstrings\n", "\n", "A [docstring](https://www.python.org/dev/peps/pep-0257/) is a string literal that occurs as the first statement in a module, function, class, or method definition to document it.\n", "\n", "All modules, classes, public methods and exported functions should include a docstring." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "def complex(real=0.0, imag=0.0):\n", " \"\"\"Form a complex number.\n", "\n", " Keyword arguments:\n", " real -- the real part (default 0.0)\n", " imag -- the imaginary part (default 0.0)\n", " \"\"\"\n", " if imag == 0.0 and real == 0.0:\n", " return complex_zero" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Code linting\n", "\n", "- _Linting_ is the process of checking code for syntactical and stylistic problems before execution.\n", "- It is useful to catch errors and improve code quality in dynamically typed, interpreted languages, where there is no compiler.\n", "- Several linters exist in the Python ecosystem. The most commonly used is [pylint](https://pylint.org/)." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Type annotations\n", "\n", "- Added in Python 3.5, [type annotations](https://www.python.org/dev/peps/pep-0484/) allow to add type hints to code entities like variables or functions, bringing a statically typed flavour to the language.\n", "- [mypy](http://mypy-lang.org/) can automatically check the code for annotation correctness." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "def greeting(name: str) -> str:\n", " return \"Hello \" + name\n", "\n", "\n", "# greeting('Alice') # OK\n", "# greeting(3) # mypy error: incompatible type \"int\"; expected \"str\"" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Unit tests\n", "\n", "Unit tests automate the testing of individual code elements like functions or methods, thus decreasing the risk of bugs and regressions.\n", "\n", "They can be implemented in Python using tools like [unittest](https://docs.python.org/3/library/unittest.html) or [pytest](https://docs.pytest.org)." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "def inc(x):\n", " return x + 1\n", "\n", "\n", "def test_answer():\n", " assert inc(3) == 5 # AssertionError: assert 4 == 5" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Packaging and dependency management" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Managing dependencies in Python\n", "\n", "- Most Python apps depend on third-party libraries and frameworks (NumPy, Flask, Requests...).\n", "- These tools may also have external dependencies, and so on.\n", "- **Dependency management** is necessary to prevent version conflicts and incompatibilities. it involves two things:\n", " - a way for the app to declare its dependencies;\n", " - a tool to resolve these dependencies and install compatible versions." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Semantic versioning\n", "\n", "- Software versioning convention used in many ecosystems.\n", "- A version number comes as a suite of three digits `X.Y.Z`.\n", " - X = major version (potentially including breaking changes).\n", " - Y = minor version (only non-breaking changes).\n", " - Z = patch.\n", "- Digits are incremented as new versions are shipped." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### pip and requirements.txt\n", "\n", "A `requirements.txt` file is the most basic way of declaring dependencies in Python.\n", "\n", "```text\n", "certifi>=2020.11.0\n", "chardet==4.0.0\n", "click>=6.5.0, <7.1\n", "download==0.3.5\n", "Flask>=1.1.0\n", "```\n", "\n", "The [pip](https://pypi.org/project/pip/) package installer can read this file and act accordingly, downloading dependencies from [PyPI](https://pypi.org/).\n", "\n", "```bash\n", "pip install -r requirements.txt\n", "```" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Virtual environments\n", "\n", "- A **virtual environment** is an isolated Python environment where a project's dependencies are installed.\n", "- Using them prevents the risk of mixing dependencies required by different projects on the same machine.\n", "- Several tools exist to manage virtual environments in Python, for example [virtualenv](https://virtualenv.pypa.io) and [conda](https://docs.conda.io)." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### conda and environment.yml\n", "\n", "Installed as part of the [Anaconda](https://www.anaconda.com/) distribution, the [conda](https://docs.conda.io) package manager reads an `environment.yml` file to install the dependencies associated to a specific virtual environment.\n", "\n", "```yaml\n", "name: example-env\n", "\n", "channels:\n", " - conda-forge\n", " - defaults\n", "\n", "dependencies:\n", " - python=3.7\n", " - matplotlib\n", " - numpy\n", "```" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Poetry\n", "\n", "[Poetry](https://python-poetry.org) is a recent packaging and dependency management tool for Python. It downloads packages from [PyPI](https://pypi.org/) by default.\n", "\n", "```bash\n", "# Create a new poetry-compliant project\n", "poetry new \n", "\n", "# Initialize an already existing project for Poetry\n", "poetry init\n", "\n", "# Install defined dependencies\n", "poetry install\n", "\n", "# Add a package to project dependencies and install it\n", "poetry add \n", "\n", "# Update dependencies to sync them with configuration file\n", "poetry update\n", "```" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Poetry and virtual environments\n", "\n", "By default, Poetry creates a virtual environment for the configured project in a user-specific folder. A standard practice is to store it in the project's folder.\n", "\n", "```bash\n", "# Tell Poetry to store the environment in the local project folder\n", "poetry config virtualenvs.in-project true\n", "\n", "# Activate the environment\n", "poetry shell\n", "```" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### The pyproject.toml file\n", "\n", "Poetry configuration file, soon-to-be standard for Python projects.\n", "\n", "```toml\n", "[tool.poetry]\n", "name = \"poetry example\"\n", "version = \"0.1.0\"\n", "description = \"\"\n", "\n", "[tool.poetry.dependencies]\n", "python = \">=3.7.1,<3.10\"\n", "jupyter = \"^1.0.0\"\n", "matplotlib = \"^3.3.2\"\n", "sklearn = \"^0.0\"\n", "pandas = \"^1.1.3\"\n", "ipython = \"^7.0.0\"\n", "\n", "[tool.poetry.dev-dependencies]\n", "pytest = \"^6.1.1\"\n", "```" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Caret requirements\n", "\n", "Offers a way to precisely define dependency versions.\n", "\n", "| Requirement | Versions allowed |\n", "| :---------: | :--------------: |\n", "| ^1.2.3 | >=1.2.3 <2.0.0 |\n", "| ^1.2 | >=1.2.0 <2.0.0 |\n", "| ~1.2.3 | >=1.2.3 <1.3.0 |\n", "| ~1.2 | >=1.2.0 <1.3.0 |\n", "| 1.2.3 | 1.2.3 only |" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### The poetry.lock file\n", "\n", "- The first time Poetry install dependencies, it creates a `poetry.lock` file that contains the exact versions of all installed packages.\n", "- Subsequent installs will use these exact versions to ensure consistency.\n", "- Removing this file and running another Poetry install will fetch the latest matching versions." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Working with notebooks" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Advantages of Jupyter notebooks\n", "\n", "- Standard format for mixing text, images and (executable) code.\n", "- Open source and platform-independant.\n", "- Useful for experimenting and prototyping.\n", "- Growing ecosystem of [extensions](https://tljh.jupyter.org/en/latest/howto/admin/enable-extensions.html) for various purposes and cloud hosting solutions ([Colaboratory](https://colab.research.google.com/), [AI notebooks](https://www.ovhcloud.com/en/public-cloud/ai-notebook/)...).\n", "- Integration with tools like [Visual Studio Code](https://code.visualstudio.com/docs/datascience/jupyter-notebooks)." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Drawbacks of Jupyter notebooks\n", "\n", "- Arbitrary execution order of cells can cause confusing errors.\n", "- Notebooks don't encourage good programming habits like modularization, linting and tests.\n", "- Being JSON-based, their versioning is more difficult than for plain text files.\n", "- Dependency management is also difficult, thus hindering reproducibility." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Collaborating with notebooks\n", "\n", "A common solution for sharing notebooks between a team is to use [Jupytext](https://jupytext.readthedocs.io). This tool can associate an `.ipynb` file with a Python file to facilitate collaboration and version control.\n", "\n", "[![Collaboration example through Jupytext](images/JupyterPyCharm.gif)](https://jupytext.readthedocs.io/en/latest/examples.html)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Code organization\n", "\n", "Monolithic notebooks can grow over time and become hard to understand and maintain.\n", "\n", "Just like in a traditional software project, it is possible to split them into separate parts, thus following the [separation of concerns](https://en.wikipedia.org/wiki/Separation_of_concerns) design principle.\n", "\n", "Code can be splitted into several sub-notebooks and/or external Python files. The latter facilitates unit testing and version control." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Notebook workflow\n", "\n", "Tools like [papermill](https://papermill.readthedocs.io) can orchestrate the execution of several notebooks in a row. External parameters can be passed to notebooks, and the runtime flow can depend on the execution results of each notebook." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "c0a5ec58377a4f3fbfd3504d6862593b", "version_major": 2, "version_minor": 0 }, "text/plain": [ "HBox(children=(IntProgress(value=0, description='Executing', max=4, style=ProgressStyle(description_width='iniā€¦" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "# Doesn't work on Google Colaboratory. Workaround here: \n", "# https://colab.research.google.com/github/rjdoubleu/Colab-Papermill-Patch/blob/master/Colab-Papermill-Driver.ipynb\n", "notebook_dir = \"./papermill\"\n", "result = pm.execute_notebook(\n", " os.path.join(notebook_dir, \"simple_input.ipynb\"),\n", " os.path.join(notebook_dir, \"simple_output.ipynb\"),\n", " parameters={\"msg\": \"Hello\"},\n", ")" ] } ], "metadata": { "celltoolbar": "Diaporama", "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.7.5" } }, "nbformat": 4, "nbformat_minor": 4 }