{ "cells": [ { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "**About this notebook**\n", "\n", "This notebook/presentation has been prepared for the 2017 edition of http://python.g-node.org, the renowned Advanced Scientific Programming in Python summer school (Happy 10th Anniversary!!). I gratefully acknowledge the efforts of the entire Python community, which produced great documentation I largely consumed to create this notebook; a list of which can be found at the end of the notebook. If I have missed anyone, apologies, let me know and I'll add you to the list!\n", "\n", "Although you should be able to run the notebook straight out of the box, bear in mind that it was designed to work with Python3, in conjunction with the following nbextensions:\n", "* RISE by https://github.com/damianavila/RISE (enables the slideshow)\n", "* Runtools by https://github.com/ipython-contrib/jupyter_contrib_nbextensions/wiki/Runtools (runs the entire notebook regardless of exceptions thrown on the way, as we will speak about recovering from errors)\n", "\n", "The repository also contains exercises, with and without solutions, which I borrowed from last year's edition of the summer school.\n", "\n", "I hope you enjoy it! By all means get in touch! :)\n", "\n", "Etienne\n", "\n", "
yield expression_list
\n",
"\n",
"_yield_ does something similar to _return_:\n",
"* _return_ gives back control to the caller function, and returns some content;\n",
"* _yield_ freezes execution temporarily, stores current context, and returns some content to .\\_\\_next\\_\\_()'s caller;\n",
"\n",
"_yield_ saves local state and variables, instruction pointer and internal evaluation stack; i.e. enough information so that .\\_\\_next\\_\\_() behaves like an external call."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0 1 1 2 3 5 8 13 21 "
]
}
],
"source": [
"def fib_with_yield(max_limit):\n",
" '''fib function using yield'''\n",
" a, b = 0, 1 # a = 0 and b = 1\n",
" while a < max_limit:\n",
" yield a # freezes execution, returns current a\n",
" a, b = b, a + b # a = b and b = a + b\n",
"\n",
"for i in fib_with_yield(33):\n",
" print(i, end=\" \")"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"\n",
"def my_generator():\n",
" ...\n",
" item = yield\n",
" ...\n",
" value = do_something(item)\n",
" ...\n",
" yield value # return value\n",
" \n",
" \n",
"gen = my_generator()\n",
"\n",
"next(gen) # Starts generator and advances to _yield_\n",
"value = gen.send(item) # Sends and receives stuff\n",
"gen.close() # Terminates\n",
"gen.throw(exc, val, tb) # Raises exception\n",
"result = yield from gen # Handles callback and returns content\n",
"
\n",
"\n",
"https://www.python.org/dev/peps/pep-0342/"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Decorators"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Functions are objects themselves."
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Hello world!\n"
]
}
],
"source": [
"def shout(word=\"hello world\"):\n",
" return word.capitalize() + \"!\"\n",
"print(shout())"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Hello world!\n"
]
}
],
"source": [
"yell = shout\n",
"print(yell())"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"name 'shout' is not defined\n",
"Hello world!\n"
]
}
],
"source": [
"del shout\n",
"try: # this is how you catch an Exception\n",
" print(shout()) # This won't work\n",
"except NameError as e:\n",
" print(e) \n",
"print(yell()) # But this still works"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Therefore, functions can be defined inside other functions."
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"hello world...\n"
]
}
],
"source": [
"def languaging():\n",
" def whisper(word=\"Hello world\"):\n",
" return word.lower() + \"...\"\n",
" print(whisper())\n",
"languaging()"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"name 'whisper' is not defined\n"
]
}
],
"source": [
"try:\n",
" print(whisper()) # is outside the scope!\n",
"except NameError as e:\n",
" print(e)"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
" /''''''\\ \n",
"@Tomatoes@\n",
"---Ham---\n",
"~~Salad~~\n",
" \\______/\n",
"
"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"\n",
" /''''''\\ \n",
"@Tomatoes@\n",
"---Ham---\n",
"~~Salad~~\n",
" \\______/\n",
"
"
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Hands off of keyboards now!\n"
]
}
],
"source": [
"countdown(1)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Solution"
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {
"collapsed": true,
"slideshow": {
"slide_type": "-"
}
},
"outputs": [],
"source": [
"def bread(my_function):\n",
" def wrapper():\n",
" print(\" /''''''\\ \")\n",
" my_function()\n",
" print(\" \\______/ \")\n",
" return wrapper\n",
"\n",
"def ingredients(my_function):\n",
" def wrapper():\n",
" print(\"@Tomatoes@\")\n",
" my_function()\n",
" print(\"~~Salad~~\")\n",
" return wrapper"
]
},
{
"cell_type": "code",
"execution_count": 38,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" /''''''\\ \n",
"@Tomatoes@\n",
"---Ham---\n",
"~~Salad~~\n",
" \\______/ \n"
]
}
],
"source": [
"@bread # Order matters\n",
"@ingredients #\n",
"def sandwich(food=\"---Ham---\"):\n",
" print(food)\n",
"sandwich()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Context managers"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Context managers are semantically related to decorators.\n",
"\n",
"They aim primarily to help you manage resources properly, i.e., groom your memory, avoid consumer bottlenecks, clean up after yourself, maintain livelihood of connections (db), etc, and other sensible things."
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"ERROR:root:Internal Python error in the inspect module.\n",
"Below is the traceback from this internal error.\n",
"\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Traceback (most recent call last):\n",
" File \"//anaconda/lib/python3.6/site-packages/IPython/core/interactiveshell.py\", line 2881, in run_code\n",
" exec(code_obj, self.user_global_ns, self.user_ns)\n",
" File \"\n",
"import time\n",
"class MyTimeIt():\n",
" def __init__(self):\n",
" ... # Write something here to initialise time\n",
" def __enter__(self):\n",
" ... # Here start the timer\n",
" def __exit__(self, *args):\n",
" ... # Here measure the amount of time\n",
"\n",
"with MyTimeIt():\n",
" time.sleep(2)\n",
"
"
]
},
{
"cell_type": "code",
"execution_count": 47,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Hands off of keyboards now!\n"
]
}
],
"source": [
"countdown(1)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Solution"
]
},
{
"cell_type": "code",
"execution_count": 49,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"This function took 2.00 seconds.\n"
]
}
],
"source": [
"import time\n",
"\n",
"class MyTimeIt():\n",
" def __init__(self):\n",
" self.t = 0.\n",
"\n",
" def __enter__(self):\n",
" self.t = time.time()\n",
"\n",
" def __exit__(self, *args):\n",
" print('This function took {:.2f} seconds.'.format(time.time() - self.t))\n",
"\n",
"with MyTimeIt():\n",
" time.sleep(2)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Take-home message\n",
"* _Iterators_ are arcane mechanisms that support loops, and everything else;\n",
"\n",
"* _Generators_ are kinds of iterators that provide a level of optimisation and interactivity;\n",
"\n",
"* _Decorators_ are a mechanism to incrementally power-up existing code;\n",
"\n",
"* _Context managers_ are semantically related to decorators, to manage resources properly."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Further reading, sources of inspiration and grateful acknowledgements\n",
"\n",
"* The Python Documentation: https://docs.python.org/\n",
"* Generators: http://intermediatepythonista.com/python-generators\n",
"* Decorators: http://sametmax.com/ for some examples\n",
"* Context managers: https://jeffknupp.com/blog/2016/03/07/python-with-context-managers/\n",
"* Zbigniew for last year's exercises!"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"