From 97c06daf22084086edc02bcd8bf157041a25cddd Mon Sep 17 00:00:00 2001 From: Jonas Eschle Date: Tue, 6 Aug 2024 17:33:52 -0400 Subject: [PATCH] enh: transfer methods notebook --- .pre-commit-config.yaml | 8 +- advanced-python/20DataAndPlotting.ipynb | 2 +- python/01basics.ipynb | 2 +- python/conditions.ipynb | 2 +- python/dictionaries.ipynb | 2 +- python/lists.ipynb | 2 +- python/methods.ipynb | 2 +- python/modules.ipynb | 902 ++++++++++++++++++++++++ python/modules.md | 430 ----------- python/numbers.ipynb | 2 +- python/strings.ipynb | 2 +- 11 files changed, 911 insertions(+), 445 deletions(-) create mode 100644 python/modules.ipynb delete mode 100644 python/modules.md diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ff869508..c60dfd19 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,13 +22,7 @@ repos: - id: nbqa-pyupgrade additional_dependencies: [ pyupgrade ] args: [ --py39-plus ] -# -# - repo: https://github.com/ambv/black -# rev: 21.9b0 -# hooks: -# - id: black -# args: [ --line-length=120 ] -# + - repo: https://github.com/kynan/nbstripout rev: 0.8.0 hooks: diff --git a/advanced-python/20DataAndPlotting.ipynb b/advanced-python/20DataAndPlotting.ipynb index 78c94786..8a30e8d2 100644 --- a/advanced-python/20DataAndPlotting.ipynb +++ b/advanced-python/20DataAndPlotting.ipynb @@ -665,7 +665,7 @@ "kernelspec": { "display_name": "Python [conda env:101python]", "language": "python", - "name": "conda-env-101python-py" + "name": "python" }, "language_info": { "codemirror_mode": { diff --git a/python/01basics.ipynb b/python/01basics.ipynb index f3dda535..f497d518 100644 --- a/python/01basics.ipynb +++ b/python/01basics.ipynb @@ -1385,7 +1385,7 @@ "kernelspec": { "display_name": "Python [conda env:101python]", "language": "python", - "name": "conda-env-101python-py" + "name": "python" }, "language_info": { "codemirror_mode": { diff --git a/python/conditions.ipynb b/python/conditions.ipynb index fa4c2ecb..c50933a8 100644 --- a/python/conditions.ipynb +++ b/python/conditions.ipynb @@ -954,7 +954,7 @@ "kernelspec": { "display_name": "Python [conda env:101python]", "language": "python", - "name": "conda-env-101python-py" + "name": "python" }, "language_info": { "codemirror_mode": { diff --git a/python/dictionaries.ipynb b/python/dictionaries.ipynb index c58e9ee8..69dc4a0f 100644 --- a/python/dictionaries.ipynb +++ b/python/dictionaries.ipynb @@ -612,7 +612,7 @@ "kernelspec": { "display_name": "Python [conda env:101python]", "language": "python", - "name": "conda-env-101python-py" + "name": "python" }, "language_info": { "codemirror_mode": { diff --git a/python/lists.ipynb b/python/lists.ipynb index 7cda00da..b9a03cd4 100644 --- a/python/lists.ipynb +++ b/python/lists.ipynb @@ -1777,7 +1777,7 @@ "kernelspec": { "display_name": "Python [conda env:101python]", "language": "python", - "name": "conda-env-101python-py" + "name": "python" }, "language_info": { "codemirror_mode": { diff --git a/python/methods.ipynb b/python/methods.ipynb index 9282af0e..bc32d594 100644 --- a/python/methods.ipynb +++ b/python/methods.ipynb @@ -1663,7 +1663,7 @@ "kernelspec": { "display_name": "Python [conda env:101python]", "language": "python", - "name": "conda-env-101python-py" + "name": "python" }, "language_info": { "codemirror_mode": { diff --git a/python/modules.ipynb b/python/modules.ipynb new file mode 100644 index 00000000..1dbe25a8 --- /dev/null +++ b/python/modules.ipynb @@ -0,0 +1,902 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "# Modules\n", + "\n", + "Python comes with lots of useful stuff, which is provided with modules\n", + "(and submodules, see later).\n", + "We have already met the maths module, but did not talk about how we started using it." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "import math" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "math" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "math.pi" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "math.sin(1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "The path after `from` might look different on your computer.\n", + "\n", + "So, `math` is a _module_, and this seems to behave a lot like other objects we\n", + "have met: it is a container with properties and methods attached that we can\n", + "access with the dot operator `.`. Actually, that is pretty much all there is to\n", + "them." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## Using modules into your code: import\n", + "\n", + "The keyword `import`, usually specified at the beginning of your source code, is\n", + "used to tell Python what modules you want to make available to your current\n", + "code.\n", + "\n", + "There are different ways of specifying an import. The one we have seen already\n", + "simply makes the module available to you:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "import random" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "random.uniform(0, 1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "The module `random` contains functions useful for random number generation: with\n", + "the `import` above, we have made the `random` module accessible, and everything\n", + "within that module is accessible via the syntax `random.`. For the record,\n", + "the `uniform(x,y)` method returns a pseudo-random number within the range\n", + "`$ [x,y] $`.\n", + "\n", + "Sometimes you want to make only one or more things from a given module\n", + "accessible: Python gives you the ability to import just those:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "from random import choice, uniform" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "uniform(0, 1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "choice([ 33, 56, 42, -1 ])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "\n", + "In this case the `uniform` and `choice` names are available _directly_, _i.e._\n", + "without using the `random` prefix. All other functions in the `random` module\n", + "are not available in this case. For the record, the `choice` function returns a\n", + "random element from a given collection.\n", + "\n", + "Another option is to import _all_ functions of a certain module and make them\n", + "available without a prefix, which you should *never* do (except in very special cases if you're writing a library)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "# from random import * # don't run this :) never" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "This is not that recommended as you generally do not know what is the extent\n", + "of what you are importing and you might end up with name clashes between your\n", + "current code and the imported module, as it will all be in the same namespace,\n", + "meaning directly available with no need for a `.` syntax. \n", + "\n", + "Lastly, it is possible to import modules, or specific names from a module,\n", + "under an alias." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "from random import uniform as uni" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "uni(0, 1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "np.arccos(1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "This option is useful when you need to assign shorter aliases to names you will\n", + "use frequently. In particular, the alias `np` for the `numpy` module will be\n", + "encountered a lot.\n", + "\n", + "Note that modules can have submodules, specified with extra dots `.`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "from pathlib import Path" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "Path('..').absolute()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "When importing a module, its **submodules are not available by default and you\n", + "must import them explicitly**:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "import os" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "os.getcwd()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "It is also possible to import several modules with a single import command:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "import math\n", + "import os\n", + "import sys" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "but this is [not recommended by the Python style guide][https://www.python.org/dev/peps/pep-0008/#imports], which\n", + "suggests to use several import statements, one per module, as it improves\n", + "readability:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "import math\n", + "import os\n", + "import sys" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "If you need to import several names from a single module, you can split an import\n", + "function over multiple lines:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "from math import e, exp, floor, log" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "floor(exp(log(e)))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "However, while these are possibilities, importing the modules is the usual way to go. If you're unsure, just look around for good examples; remember, consistency is the key!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## The standard library\n", + "\n", + "The set of things that Python comes with, from all of the types of objects to\n", + "all of the different modules, is called the [standard library][stl]. It is\n", + "recommended to browse through the standard library documentation to see what is\n", + "available: Python is rich of standard modules, and you should reuse them as much\n", + "as possible instead of rewriting code on your own.\n", + "\n", + "Some of the categories for which standard modules are available are:\n", + "\n", + "* processing paths\n", + "* date and time manipulation\n", + "* mathematical functions\n", + "* parsing of certain file formats\n", + "* support for multiple processes and threads\n", + "* ...\n", + "\n", + "Use standard Python library modules with confidence: being part of any standard\n", + "Python distribution, your code will be easily portable." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## Modules from PyPi\n", + "\n", + "Many external modules can be found on [PyPi][pypi], the Python Package Index repository.\n", + "\n", + "If a certain module you need is not available on your distribution you can\n", + "easily install it with the `pip` shell command as seen in the previous lectures." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## (Advanced) Write your first Python module\n", + "\n", + "The simplest Python module you can write is just a `.py` file with some\n", + "functions inside:\n", + "\n", + "\n", + "```python\n", + "# myfirstmodule.py\n", + "\n", + "def one():\n", + " print('this is my first function')\n", + "\n", + "def two():\n", + " print('this is my second function')\n", + "```\n", + "\n", + "You can now fire an `ipython` shell and use those functions right away (because it automatically picks up modules aka `.py` files inside the working directory)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "```python\n", + ">>> import myfirstmodule\n", + ">>> myfirstmodule.one()\n", + "this is my first function\n", + ">>> myfirstmodule.two()\n", + "this is my second function\n", + "```\n", + "\n", + "By simply calling the file `myfirstmodule.py` we have made it available as a\n", + "module named `myfirstmodule` - given that the file is in the same directory\n", + "where we have launched the Python interpreter.\n", + "\n", + "### Module name restrictions\n", + "\n", + "Note that you cannot pick any name you want for a module! From the\n", + "[Python style guide][https://www.python.org/dev/peps/pep-0008/#package-and-module-names\n", + "], we gather that we should use \"short,\n", + "all-lowercase names\". As a matter of fact, if we used dashes in the file name,\n", + "we would have ended up with a syntax error while trying to load it:" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "```python\n", + ">>> import my-first-module\n", + " File \"\", line 1\n", + " import my-first-module\n", + " ^\n", + "SyntaxError: invalid syntax\n", + "```\n", + "\n", + "Python treats `-` as a minus and does not understand your intentions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "{% endcallout %}\n", + "\n", + "\n", + "## Write a structured module\n", + "\n", + "Let's now create a more structured module, with submodules and different files.\n", + "We can start from the `myfirstmodule.py` file and create a directory structure:\n", + "\n", + "```bash\n", + "$ mkdir yabba\n", + "$ cp myfirstmodule.py yabba/__init__.py\n", + "```\n", + "\n", + "We have reused the same file created before, copied it into a directory called\n", + "`yabba` and renamed it to `__init__.py`. The double underscore should ring a\n", + "bell: this is a Python special name, and it represents the \"main file\" within\n", + "a module, whereas the directory name now represents the module name.\n", + "\n", + "This means that our module is called `yabba`, and if we import it, functions\n", + "from `__init__.py` will be available:\n", + "\n", + "```python\n", + ">>> import yabba\n", + ">>> yabba.one()\n", + "this is my first function\n", + ">>> yabba.two()\n", + "this is my second function\n", + "```\n", + "\n", + "We can create an additional file inside the `yabba` directory, say\n", + "`yabba/extra.py` and have more functions there:\n", + "\n", + "```python\n", + "# yabba/extra.py\n", + "\n", + "def three():\n", + " print 'this function will return the number three'\n", + " return 3\n", + "```\n", + "\n", + "We have effectively made `extra` a submodule of `yabba`. Let's try:\n", + "\n", + "```python\n", + ">>> import yabba\n", + ">>> filter(lambda x: not x.startswith('__'), dir(yabba))\n", + "['one', 'two']\n", + ">>> import yabba.extra\n", + ">>> yabba.extra.three()\n", + "yabba.extra.three()\n", + "this function will return the number three\n", + "3\n", + "```\n", + "\n", + "{% challenge \"What have I done with the filter function?\" %}\n", + "\n", + "We have used the filter function above to list the functions we have defined\n", + "in our module. Can you describe in detail what the commands above do?\n", + "{% solution \"Solution\" %}\n", + "\n", + "The `dir(module)` command lists all _names_ (not necessarily functions, not\n", + "necessarily defined by us) contained in a given imported module. We have used the\n", + "`filter()` command to filter out all names starting with two underscores. Every\n", + "item returned by `dir()` is passed as `x` to the lambda function which returns\n", + "`True` or `False`, determining whether the `filter()` function should keep or\n", + "discard the current element.\n", + "\n", + "{% endsolution %}\n", + "\n", + "{% endchallenge %}\n", + "\n", + "\n", + "## Run a module\n", + "\n", + "We can make a Python module that can be easily imported by other Python\n", + "programs, but we can also make it in a way that it can be run directly as a\n", + "Python script.\n", + "\n", + "Let's write this special module and call it `runnable.py`:\n", + "\n", + "```python\n", + "#!/usr/bin/env python\n", + "\n", + "long_format = False\n", + "\n", + "def print_label(label, msg):\n", + " if long_format:\n", + " out = '{0}: {1}'.format(label.upper(), str(msg))\n", + " else:\n", + " out = '{0}-{1}'.format(label[0].upper(), str(msg))\n", + " print out\n", + "\n", + "def debug(msg):\n", + " print_label('debug', msg)\n", + "\n", + "def warning(msg):\n", + " print_label('warning', msg)\n", + "\n", + "if __name__ == '__main__':\n", + " print '*** Testing print functions ***'\n", + " debug('This is a debug message')\n", + " long_format = True\n", + " warning('This is a warning message with a long label')\n", + "else:\n", + " print 'Module {0} is being imported'.format(__name__)\n", + "```\n", + "\n", + "Now let's make it executable:\n", + "\n", + "```bash\n", + "$ chmod +x runnable.py\n", + "```\n", + "\n", + "It can be now run as a normal executable from your shell:\n", + "\n", + "```\n", + "$ ./runnable.py\n", + "*** Testing print functions ***\n", + "D-This is a debug message\n", + "WARNING: This is a warning message with a long label\n", + "```\n", + "\n", + "There are two outstanding notions here. First off, the first line is a\n", + "\"shebang\": it really has to be the _first_ line in a file (it cannot be the\n", + "second, or \"one of the first\", or the first non-empty) and it basically tells\n", + "your shell that your executable text file has to be interpreted by the current\n", + "Python interpreter. Just use this line as it is.\n", + "\n", + "Secondly, we notice we have a peculiar `if` condition with a block that gets\n", + "executed when we run the file. `__name__` is a special internal Python variable\n", + "which is set to the module name in case the module is imported. When the module\n", + "is ran, it is set to the special value `\"__main__\"`.\n", + "\n", + "The `else:` condition we have added is just to show what happens when you import\n", + "the module instead:\n", + "\n", + "```python\n", + ">>> import runnable\n", + "Module runnable is being imported\n", + ">>> runnable.warning('hey I can use it from here too')\n", + "W-hey I can use it from here too\n", + "```\n", + "\n", + "Now, the `if` condition is not necessary when you want to run the module - those\n", + "lines in the `if` block will be executed anyway. It is however used to _prevent_\n", + "some lines from being executed when you import the file as a module.\n", + "\n", + "Please also note that module imports are typically _silent_, so the `else:`\n", + "condition with a printout would not exist in real life.\n", + "\n", + "\n", + "[stl]: https://docs.python.org/2/library/index.html\n", + "[pep8-import]: https://www.python.org/dev/peps/pep-0008/#imports\n", + "[pep8-modulenames]: https://www.python.org/dev/peps/pep-0008/#package-and-module-names\n", + "[pypi]: https://pypi.org/\n", + "[anaconda]: https://www.anaconda.com/distribution/\n", + "[lcg_virtualenv]: https://gitlab.cern.ch/cburr/lcg_virtualenv/\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [conda env:101python]", + "language": "python", + "name": "python" + }, + "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.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/python/modules.md b/python/modules.md deleted file mode 100644 index 4a318657..00000000 --- a/python/modules.md +++ /dev/null @@ -1,430 +0,0 @@ -# Modules - -Python comes with lots of useful stuff, which is provided with modules -(and submodules, see later). -We have already met the maths module, but did not talk about how we started using it. - -```python ->>> import math ->>> math - ->>> math.pi -3.141592653589793 ->>> math.sin(1) -0.8414709848078965 -``` - -The path after `from` might look different on your computer. - -So, `math` is a _module_, and this seems to behave a lot like other objects we -have met: it is a container with properties and methods attached that we can -access with the dot operator `.`. Actually, that is pretty much all there is to -them. - - -## Using modules into your code: import - -The keyword `import`, usually specified at the beginning of your source code, is -used to tell Python what modules you want to make available to your current -code. - -There are different ways of specifying an import. The one we have seen already -simply makes the module available to you: - -```python ->>> import random ->>> random.uniform(0, 1) -0.5877109428927353 -``` - -The module `random` contains functions useful for random number generation: with -the `import` above, we have made the `random` module accessible, and everything -within that module is accessible via the syntax `random.`. For the record, -the `uniform(x,y)` method returns a pseudo-random number within the range -`$ [x,y] $`. - -Sometimes you want to make only one or more things from a given module -accessible: Python gives you the ability to import just those: - -```python ->>> from random import uniform, choice ->>> uniform(0, 1) -0.4059007502204043 ->>> choice([ 33, 56, 42, -1 ]) -42 -``` - -In this case the `uniform` and `choice` names are available _directly_, _i.e._ -without using the `random` prefix. All other functions in the `random` module -are not available in this case. For the record, the `choice` function returns a -random element from a given collection. - -Another option is to import _all_ functions of a certain module and make them -available without a prefix: - -```python ->>> from random import * ->>> gauss(0, 1) --1.639334770284028 -``` - -This is not that recommended as you generally do not know what is the extent -of what you are importing and you might end up with name clashes between your -current code and the imported module, as it will all be in the same namespace, -meaning directly available with no need for a `.` syntax. - -Lastly, it is possible to import modules, or specific names from a module, -under an alias. - -```python ->>> from random import uniform as uni ->>> uni(0, 1) -0.7288973406605329 ->>> import numpy as np -np.arccos(1) -0.0 -``` - -This option is useful when you need to assign shorter aliases to names you will -use frequently. In particular, the alias `np` for the `numpy` module will be -encountered a lot. - -Note that modules can have submodules, specified with extra dots `.`: - -```python ->>> from os.path import abspath ->>> abspath('..') -'/afs/cern.ch/user/d' -``` - -When importing a module, its **submodules are not available by default and you -must import them explicitly**: - -```python ->>> import os ->>> os.getcwd() -'/afs/cern.ch/user/d/dberzano' ->>> import os.path ->>> os.path.basename(os.getcwd()) -'dberzano' -``` - -Note that due to the current Python implementation of the `os` module, `os.path` -functions are _actually_ available _even without importing `os.path`. But just -`os`_. You cannot and should not rely on this implementation, which represents an exception -and might change in the future. Always import submodules explicitly! - -It is also possible to import several modules with a single import command: - -```python ->>> import os, sys, math -``` - -but this is [not recommended by the Python style guide][pep8-import], which -suggests to use several import statements, one per module, as it improves -readability: - -```python ->>> import os ->>> import sys ->>> import math -``` - -If you need to import several names from a single module, you can split an import -function over multiple lines: - -```python ->>> from math import ( -... exp, -... log, -... e, -... floor -... ) ->>> floor(exp(log(e))) -2.0 -``` - - -## The standard library - -The set of things that Python comes with, from all of the types of objects to -all of the different modules, is called the [standard library][stl]. It is -recommended to browse through the standard library documentation to see what is -available: Python is rich of standard modules, and you should reuse them as much -as possible instead of rewriting code on your own. - -Some of the categories for which standard modules are available are: - -* processing paths -* date and time manipulation -* mathematical functions -* parsing of certain file formats -* support for multiple processes and threads -* ... - -Use standard Python library modules with confidence: being part of any standard -Python distribution, your code will be easily portable. - - -## Modules from PyPi - -Many external modules can be found on [PyPi][pypi], the Python Package Index repository. -Some of those modules are -already part of some Python distributions (such as [Anaconda][anaconda], which -comes with more than a thousand science-oriented modules preinstalled). - -If a certain module you need is not available on your distribution you can -easily install it with the `pip` shell command. Since you typically do not have write -access to the standard Python installation's directories, `pip` allows you to -install modules only for yourself, under your current user's home directory. -It is recommended to set up in your shell startup script (such as `~/.bashrc`) -the following two lines telling once and for all where to install and search for -Python user modules: - -```bash -export PYTHONUSERBASE=$HOME/.local -export PATH=$PYTHONUSERBASE/bin:$PATH -``` - -Once you have done that, close your current terminal window and open a new one, -and you will be ready to use `pip`. We will see in a later lesson how to install -the `root_pandas` module with: - -```bash -pip install --user root_pandas -``` - -## Modules inside a virtual environment - -It is however usually preferable and safer to do everything inside a virtual environement. -The latter is like a copy of your current environement. Thus you can modify your virtual -environement (including installing/deleting/updating modules) without affecting your default -environement. If at some point you realize you have broken everything, you can always exit -the virtual environement and go back to the default lxplus one. - -To build a virtual environement based on LCG views, you can use [LCG_virtualenv][lcg_virtualenv]: - -```bash -git clone https://gitlab.cern.ch/cburr/lcg_virtualenv.git -./lcg_virtualenv/create_lcg_virtualenv myVenv -``` -To activate the virtual environement do: - -```bash -source myVenv/bin/activate -``` - -You can then install stuff with `pip`, like for instance `root_pandas`: - -```bash -pip install --upgrade root_pandas matplotlib -python -c 'import pandas; print(f"Got pandas from {pandas.__file__}")' -python -c 'import root_pandas; print(f"Got root_pandas from {root_pandas.__file__}")' -python -c 'import matplotlib; print(f"Got matplotlib from {matplotlib.__file__}")' -``` - -You can go back to the default environement using the `deactivate` command. - - -## Write your first Python module - -The simplest Python module you can write is just a `.py` file with some -functions inside: - -```python -# myfirstmodule.py - -def one(): - print('this is my first function') - -def two(): - print('this is my second function') -``` - -You can now fire an `ipython` shell and use those functions right away: - -```python ->>> import myfirstmodule ->>> myfirstmodule.one() -this is my first function ->>> myfirstmodule.two() -this is my second function -``` - -By simply calling the file `myfirstmodule.py` we have made it available as a -module named `myfirstmodule` - given that the file is in the same directory -where we have launched the Python interpreter. - -{% callout "Module name restrictions" %} - -Note that you cannot pick any name you want for a module! From the -[Python style guide][pep8-modulenames], we gather that we should use "short, -all-lowercase names". As a matter of fact, if we used dashes in the file name, -we would have ended up with a syntax error while trying to load it: - -```python ->>> import my-first-module - File "", line 1 - import my-first-module - ^ -SyntaxError: invalid syntax -``` - -Python treats `-` as a minus and does not understand your intentions. - -{% endcallout %} - - -## Write a structured module - -Let's now create a more structured module, with submodules and different files. -We can start from the `myfirstmodule.py` file and create a directory structure: - -```bash -$ mkdir yabba -$ cp myfirstmodule.py yabba/__init__.py -``` - -We have reused the same file created before, copied it into a directory called -`yabba` and renamed it to `__init__.py`. The double underscore should ring a -bell: this is a Python special name, and it represents the "main file" within -a module, whereas the directory name now represents the module name. - -This means that our module is called `yabba`, and if we import it, functions -from `__init__.py` will be available: - -```python ->>> import yabba ->>> yabba.one() -this is my first function ->>> yabba.two() -this is my second function -``` - -We can create an additional file inside the `yabba` directory, say -`yabba/extra.py` and have more functions there: - -```python -# yabba/extra.py - -def three(): - print 'this function will return the number three' - return 3 -``` - -We have effectively made `extra` a submodule of `yabba`. Let's try: - -```python ->>> import yabba ->>> filter(lambda x: not x.startswith('__'), dir(yabba)) -['one', 'two'] ->>> import yabba.extra ->>> yabba.extra.three() -yabba.extra.three() -this function will return the number three -3 -``` - -{% challenge "What have I done with the filter function?" %} - -We have used the filter function above to list the functions we have defined -in our module. Can you describe in detail what the commands above do? -{% solution "Solution" %} - -The `dir(module)` command lists all _names_ (not necessarily functions, not -necessarily defined by us) contained in a given imported module. We have used the -`filter()` command to filter out all names starting with two underscores. Every -item returned by `dir()` is passed as `x` to the lambda function which returns -`True` or `False`, determining whether the `filter()` function should keep or -discard the current element. - -{% endsolution %} - -{% endchallenge %} - - -## Run a module - -We can make a Python module that can be easily imported by other Python -programs, but we can also make it in a way that it can be run directly as a -Python script. - -Let's write this special module and call it `runnable.py`: - -```python -#!/usr/bin/env python - -long_format = False - -def print_label(label, msg): - if long_format: - out = '{0}: {1}'.format(label.upper(), str(msg)) - else: - out = '{0}-{1}'.format(label[0].upper(), str(msg)) - print out - -def debug(msg): - print_label('debug', msg) - -def warning(msg): - print_label('warning', msg) - -if __name__ == '__main__': - print '*** Testing print functions ***' - debug('This is a debug message') - long_format = True - warning('This is a warning message with a long label') -else: - print 'Module {0} is being imported'.format(__name__) -``` - -Now let's make it executable: - -```bash -$ chmod +x runnable.py -``` - -It can be now run as a normal executable from your shell: - -``` -$ ./runnable.py -*** Testing print functions *** -D-This is a debug message -WARNING: This is a warning message with a long label -``` - -There are two outstanding notions here. First off, the first line is a -"shebang": it really has to be the _first_ line in a file (it cannot be the -second, or "one of the first", or the first non-empty) and it basically tells -your shell that your executable text file has to be interpreted by the current -Python interpreter. Just use this line as it is. - -Secondly, we notice we have a peculiar `if` condition with a block that gets -executed when we run the file. `__name__` is a special internal Python variable -which is set to the module name in case the module is imported. When the module -is ran, it is set to the special value `"__main__"`. - -The `else:` condition we have added is just to show what happens when you import -the module instead: - -```python ->>> import runnable -Module runnable is being imported ->>> runnable.warning('hey I can use it from here too') -W-hey I can use it from here too -``` - -Now, the `if` condition is not necessary when you want to run the module - those -lines in the `if` block will be executed anyway. It is however used to _prevent_ -some lines from being executed when you import the file as a module. - -Please also note that module imports are typically _silent_, so the `else:` -condition with a printout would not exist in real life. - - -[stl]: https://docs.python.org/2/library/index.html -[pep8-import]: https://www.python.org/dev/peps/pep-0008/#imports -[pep8-modulenames]: https://www.python.org/dev/peps/pep-0008/#package-and-module-names -[pypi]: https://pypi.org/ -[anaconda]: https://www.anaconda.com/distribution/ -[lcg_virtualenv]: https://gitlab.cern.ch/cburr/lcg_virtualenv/ diff --git a/python/numbers.ipynb b/python/numbers.ipynb index 771a8e92..56572af3 100644 --- a/python/numbers.ipynb +++ b/python/numbers.ipynb @@ -289,7 +289,7 @@ "kernelspec": { "display_name": "Python [conda env:101python]", "language": "python", - "name": "conda-env-101python-py" + "name": "python" }, "language_info": { "codemirror_mode": { diff --git a/python/strings.ipynb b/python/strings.ipynb index 84e4f210..f9da5dca 100644 --- a/python/strings.ipynb +++ b/python/strings.ipynb @@ -677,7 +677,7 @@ "kernelspec": { "display_name": "Python [conda env:101python]", "language": "python", - "name": "conda-env-101python-py" + "name": "python" }, "language_info": { "codemirror_mode": {