{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# A (very short) introduction to `python`\n",
    "\n",
    "*This notebook is taken from a course of Numerical Physics at Ecole Polytechnique, many thanks to his author Michel Ferrero (CPHT, Ecole Polytechnique) for his authorisation to distribute it.*\n",
    "\n",
    "This notebook is a very quick introduction to `python`. More than a real introduction it will rather be\n",
    "a collection of small code examples that show how to manipulate the most common objects in `python`.\n",
    "For a more complete introduction, you can for example follow the\n",
    "[official python tutorial](http://docs.python.org/3/tutorial/). There are also more useful links\n",
    "at the end of this notebook.\n",
    "\n",
    "There are several ways to execute `python` code. You can execute code interactively or by running a\n",
    "script.\n",
    "\n",
    "## Interactive shell mode\n",
    "\n",
    "To run `python` interactively, you can type the following command from a shell:\n",
    "\n",
    "```bash\n",
    "$ python\n",
    "Python 3.6.2 (default, Jul 19 2017, 13:09:21) \n",
    "[GCC 7.1.1 20170622 (Red Hat 7.1.1-3)] on linux\n",
    "Type \"help\", \"copyright\", \"credits\" or \"license\" for more information.\n",
    ">>> \n",
    "```\n",
    "\n",
    "This will bring you in interactive mode. You can type commands one after the other\n",
    "and they are executed on the fly. You can have access to the result of the last\n",
    "operation using the `_` symbol. You might also want to use a slightly different\n",
    "interpreter called `ipython`, which is essentially an improved version of the standard\n",
    "`python` interpreter but with some extra features that make it more convenient to\n",
    "use interactively. Example:\n",
    "\n",
    "```python\n",
    ">>> x = 2\n",
    ">>> 12*x\n",
    "24\n",
    ">>> _ - 10\n",
    "14\n",
    "```\n",
    "\n",
    "You can leave the interactive mode either with the `quit()` command or by typing `ctrl+d`.\n",
    "\n",
    "\n",
    "## Script mode\n",
    "\n",
    "The script mode is what you will usually use once you have developed a code and want to\n",
    "run it in *production*. You would save your code in a file (with an extension `.py`),\n",
    "for example `my_script.py`, and run it with `python`. You do this from a shell with:\n",
    "\n",
    "```bash\n",
    "$ python my_script.py\n",
    "```\n",
    "\n",
    "The result of the script will be printed on the screen. After the script has been\n",
    "executed you are back in the shell. You can add the `-i` option and then, after\n",
    "the execution of the script, you will be in interactive `python`. This can be\n",
    "useful when you debug code and what to check values of variables, etc.\n",
    "\n",
    "\n",
    "## The `jupyter` notebook\n",
    "\n",
    "This is what you are looking at right now. Most of the hands-on part of this lecture\n",
    "will be done within `jupyter` notebooks. Notebooks are a convenient way to put\n",
    "text, formulas, figures and code in the same place. They work very much like a `mathematica`\n",
    "notebook. In order to execute the code in a cell:\n",
    "\n",
    "- click on the cell to activate it\n",
    "- execute the code with `shift+enter`\n",
    "\n",
    "You can try to execute the cell below. As you will see, the advantage of the notebooks\n",
    "is that the result of commands, even plots, directly appears and stays in the notebook."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"Hello world\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## A first code in `python`\n",
    "\n",
    "Let's start with a first example, a very simple `python` code that computes $\\pi$ using\n",
    "the Wallis formula\n",
    "\n",
    "\\begin{equation}\n",
    "    \\pi = 2 \\prod_{i=1}^{\\infty} \\frac{4 i^2}{4 i^2-1}\n",
    "\\end{equation}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# This code computes pi using the Wallis formula\n",
    "pi = 2.0\n",
    "N = 10000\n",
    "\n",
    "for i in range(1, N):\n",
    "    pi *= 4 * i**2\n",
    "    pi /= (4 * i**2 - 1.0)\n",
    "    \n",
    "print(\"Estimate for pi (Wallis) =\", pi)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There are several things you can observe.\n",
    "\n",
    "- `python` is a dynamically typed language. This means that there is no\n",
    "  need to give variables a type like in a compiled language.\n",
    "  When you write `pi = 2.0` the type of `pi` is defined on the fly (here it is a `float`).\n",
    "  Similarly, `N = 10000` defines an `integer`.\n",
    "\n",
    "- Comments in the code start with `#`\n",
    "\n",
    "- The block structure of a `python` script is entirely defined from the\n",
    "  **indentation**. Lines that are indented by the same amount belong to the same\n",
    "  logical block. This is a very nice feature because codes automatically *look\n",
    "  good*!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Standard types and operations\n",
    "\n",
    "There are 4 types of built-in numeric literals in `python`:\n",
    "`float`, `integer`, `bool` (boolean) and `complex`. A boolean variable can only\n",
    "take values `True` and `False` and have corresponding integer value 1 and 0.\n",
    "Here are some examples of simple operations on different types"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = 9                        # x is an integer\n",
    "print(\"type(x) ==\", type(x)) # an object's type is found using: type(variable)\n",
    "\n",
    "print(\"9/2 =\", x/2)          # division of two integers produces a float \n",
    "                             # WARNING: in python 2.7 this is an integer division (// in python 3)\n",
    "\n",
    "print(\"9/2.0 =\", x/2.0)      # this also does a float division\n",
    "print(\"9//2 =\", 9//2)        # this does integer division, the equivalent of math.floor(9/2)\n",
    "print(x**2)                  # this is how to get x to the power of 2\n",
    "\n",
    "x = 2.3                      # the same variable can be reused with a different type (a float now)\n",
    "y = 4.3e-1 * x               # note the syntax 4.3e-1 = 4.3 * 10^-1\n",
    "y *= 2                       # this means y = y * 2, same with += -= /=\n",
    "print(y)\n",
    "\n",
    "i = 1j                       # this is how to define a complex number i\n",
    "print(\"i**2 =\", i**2)\n",
    "\n",
    "a = 1+2j\n",
    "print(a.real, a.imag)        # this is how to access real and imaginary parts\n",
    "print(a.conjugate())         # complex conjugation\n",
    "print(a * (3.2+4.2j))        # an operation for complex numbers\n",
    "\n",
    "check = 9 > 4                # check is a boolean\n",
    "if check:\n",
    "    print(check, \"9 > 4\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Containers\n",
    "\n",
    "There are several kinds of containers in `python`. The most common ones are\n",
    "lists, tuples and dictionaries. Here I will describe lists, but you might\n",
    "want to read more about the other containers in a more complete\n",
    "`python` tutorial."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Lists are defined with []\n",
    "# Note that indices start at 0 (not 1 like in Fortran or Matlab)\n",
    "lst = [1, 2, 3, 4, 5, 6]\n",
    "print(\"The second element of lst is\", lst[1])\n",
    "\n",
    "# You can slice lists. [1:4] means from element 1 to 3\n",
    "print(\"Slice of lst\", lst[1:4])\n",
    "print(\"Until the end\", lst[1:])\n",
    "\n",
    "# You can use negative indices\n",
    "print(\"The last element of lst is\", lst[-1])\n",
    "\n",
    "# You can modify an element of a list and mix types\n",
    "lst[0] = 'a'\n",
    "print(\"Modified list:\", lst)\n",
    "\n",
    "# WARNING: lists are not vectors, adding lists appends them\n",
    "lst2 = [7, 8]\n",
    "lst3 = lst + lst2\n",
    "print(\"lst3 is\", lst3)\n",
    "\n",
    "# The very common list of successive integers can be generated by range\n",
    "# WARNING: range(5) yields integers from 0 to 4\n",
    "print(\"First 5 integers:\", list(range(5)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Flow control: `for` loops\n",
    "\n",
    "`python` allows to easily loop over any list. Below are\n",
    "some common examples. See how the code is indented. You can also\n",
    "build loops with the `while` command, see a more complete tutorial\n",
    "for details."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Look how indentation is used in python to define code blocks\n",
    "# WARNING: note that range(5) produces numbers from 0 to 4\n",
    "# Python treats ranges as [begin, end)\n",
    "x = 1\n",
    "for i in range(5):\n",
    "    x = x + i\n",
    "    print(\"i =\", i, \", x =\", x)\n",
    "\n",
    "# You can actually loop over any list\n",
    "color_list = ['red', 'green', 'blue']\n",
    "for col in color_list:\n",
    "    print(col)\n",
    "    \n",
    "# This can be useful to get both index and content\n",
    "for i, col in enumerate(color_list):\n",
    "    print(\"index\", i, \", color\", col)\n",
    "    \n",
    "# You can even loop over multiple lists simultaneously using zip\n",
    "xs = [0.1, 0.2, 0.3]\n",
    "ys = [1+1j, -2+3.1j, 3.4e-4+0.07j]\n",
    "for x, y in zip(xs, ys):\n",
    "    print(x, y)\n",
    "    \n",
    "# for loops can also be used to compactly create lists\n",
    "lst = [x**0.5 for x in range(10)]\n",
    "print(lst)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Flow control: `if`, `elif`, `else`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Comparing symbols are == (equal), != (not equal), >, <, <=, >=, etc.\n",
    "# See the modified range call with a step of 2\n",
    "for i in range(0, 10, 2):\n",
    "    if i == 4:\n",
    "        print(\"i is 4\")\n",
    "    elif i == 6:\n",
    "        print(\"i is 6\")\n",
    "    else:\n",
    "        print(\"i is not 4 or 6\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Defining a function\n",
    "\n",
    "Functions are defined with the `def` keyword. They can *optionally* return a value\n",
    "with the `return` keyword."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# Define a new function (this one does not return anything)\n",
    "def print_hello(name):\n",
    "    print(\"Hello,\", name)\n",
    "\n",
    "print_hello(\"John\")\n",
    "\n",
    "# This function returns a number and has an optional argument\n",
    "def fnct(x, y=5):\n",
    "    sol = x**2 - y\n",
    "    return sol\n",
    "\n",
    "x1 = fnct(3)\n",
    "print(\"With no second argument:\", x1)\n",
    "x2 = fnct(3, 1)\n",
    "print(\"With a second argument:\", x2)\n",
    "\n",
    "\n",
    "# You can document what a functions does by writing a \"\"\"docstring\"\"\" as the first line\n",
    "def cube(x):\n",
    "    \"\"\"Calculates the cube of a number\"\"\" # this is called a docstring\n",
    "    return x**3\n",
    "\n",
    "# Python's built-in help() function will now display the docstring\n",
    "print('\\nCalling help(cube) prints:')\n",
    "help(cube)\n",
    "\n",
    "# Functions are first-class objects in Python, meaning you can pass them into other functions \n",
    "# just like you can with any other variable\n",
    "def summation(func, xs):\n",
    "    \"\"\"Applies the function 'func' to each element of xs, then sums each element\"\"\"\n",
    "    return sum([func(x) for x in xs])\n",
    "\n",
    "def square(x): return x**2\n",
    "\n",
    "numbers = [1, 2, 3, 4, 5]\n",
    "print('Sum of squares:', summation(square, numbers))\n",
    "print('Sum of cubes:  ', summation(cube, numbers))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lambda functions\n",
    "\n",
    "Python has another way of defining a function, it's called a `lambda` function. It's an anonymous (unnamed) function whose only advantage over regular functions is that you can define it within an expression. They are typically used for short, simple functions that are only used once in a program. A `lambda` function can take an arbitrary number of variables and always contains an expression which is returned: `lambda x, y, z: x**2 + y**2 + z**2`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# We wish to sort a list of tuples,\n",
    "items = [(1, 'orange'), (2, 'apple'), (3, 'banana')]\n",
    "\n",
    "# BUT using the second element of each tuple as the sorting key.\n",
    "# We could define a regular function called 'second'\n",
    "def second(pair): return pair[1]\n",
    "items.sort(key = second)\n",
    "print(items)\n",
    "\n",
    "# Since we will probably not use 'second' in any other part of our program,\n",
    "# it might be better to use a lambda\n",
    "items.sort(key = lambda pair: pair[1])\n",
    "\n",
    "# lambda functions are also convenient if we want to apply an operation to\n",
    "# all elements of a list with \"map\"\n",
    "lst = range(4)\n",
    "lst_sq = list(map(lambda x: x**2, lst)) # though you may prefer the equivalent: lst_sq = [x**2 for x in lst]\n",
    "print(lst, '\\n', lst_sq)\n",
    "\n",
    "\n",
    "# WARNING\n",
    "# Although it's possible to assign a variable to a lambda function,\n",
    "# this is considered bad practice (PEP 8 - Python's Style Guide)!\n",
    "# Given that a lambda function is anonymous (no internal attribute __name__),\n",
    "# it can be more difficult to quickly spot where an error occured.\n",
    "\n",
    "# Take for example the following two definitions of the function \"dist\"\n",
    "dist = lambda x, y, z: x**2 + y**2 + z**2\n",
    "print(dist(2, 3, 4))\n",
    "# dist(1, 2)      # produces TypeError: <lambda>() missing 1 required positional argument: 'z'\n",
    "# Which lambda produced the error?       ^^^^^^\n",
    "\n",
    "def dist(x, y, z): return x**2 + y**2 + z**2\n",
    "# dist(1, 2)      # produces TypeError: dist() missing 1 required positional argument: 'z'\n",
    "# Easier to see which func caused error ^^^^"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Importing modules\n",
    "\n",
    "One of the strength of `python` is the very large number of libraries provided\n",
    "by the community. Here is how to import and use functions from a library."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import math             # import the math library\n",
    "import numpy as np      # import the numpy library recognized as np\n",
    "from time import sleep  # only import the sleep function from the time library\n",
    "\n",
    "print(math.exp(1.0))    # calls the exp function in the math library\n",
    "print(np.cos(np.pi))    # calls the cos function in numpy\n",
    "sleep(2)                # calls sleep in the time library"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Object-oriented programming:  `class`\n",
    "\n",
    "This is for those that already know about object-oriented languages. In python, just like C++ for example, you can define your own classes. Here's an example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# A new class\n",
    "# Note that all member functions must have \"self\" as a first argument\n",
    "class MyObject:\n",
    "    \"\"\"Example of a python class\"\"\"\n",
    "    def __init__(self, x):              # The constructor is called __init__\n",
    "        self.x = x\n",
    "        \n",
    "    def what_is_x(self):\n",
    "        \"\"\"Prints the value of x\"\"\"     # the documentation is given inside triple quotation marks\n",
    "        print(\"x is\", self.x)\n",
    "        \n",
    "    def change_x(self, x):\n",
    "        \"\"\"Changes the value of x\n",
    "        \n",
    "        Keyword arguments:\n",
    "        x -- the new value for x\n",
    "        \"\"\"\n",
    "        self.x = x\n",
    "        \n",
    "A = MyObject(10)\n",
    "A.what_is_x()\n",
    "A.change_x(12)\n",
    "A.what_is_x()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Getting help\n",
    "\n",
    "When you put a question mark after a command and type `ctrl-enter` it gives the help. If you type\n",
    "the parenthesis and then press `tab` it will tell you what arguments are expected."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "np.array?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## More information\n",
    "\n",
    "- The official `python` [webpage](https://www.python.org)\n",
    "- The official `python` [tutorial](https://docs.python.org/3/tutorial/index.html)\n",
    "- A very nice [tutorial](http://www.scipy-lectures.org/intro/language/python_language.html) from the scipy lectures\n",
    "- The `python` [language reference](https://docs.python.org/3/reference/index.html)\n",
    "- The `python` [standard library](https://docs.python.org/3/library/index.html)\n",
    "- The `python` [coding style guide (PEP 8)](https://www.python.org/dev/peps/pep-0008/)"
   ]
  }
 ],
 "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.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
