Commit 9ef3e8b1 authored by Frank Hellmann's avatar Frank Hellmann
Browse files

Frank's lesson material

parent da3db119
This diff is collapsed.
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"# Functions, Modules and all that...\n",
"We have already used plenty of functions in python yesterday, now it's time to define our own and see how we can work with functions in Python."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# The syntax of function definitions in python:\n",
"def fahrenheit_to_kelvin(temperature, verbose=False):\n",
" temp_in_kelvin = (temperature - 32) * 5/9 + 273.15\n",
" if verbose:\n",
" print(f\"The temperature {temperature}F equals {temp_in_kelvin:0.5}K.\")\n",
" # print(\"The temperature {}F equals {}K.\".format(temperature, temp_in_kelvin))\n",
" return temp_in_kelvin"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The temperature 30F equals 272.04K.\n"
]
},
{
"data": {
"text/plain": [
"272.0388888888889"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"fahrenheit_to_kelvin(30, verbose=True)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"272.0388888888889"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"fahrenheit_to_kelvin(30)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The temperature 30F equals 272.04K.\n"
]
},
{
"data": {
"text/plain": [
"272.0388888888889"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"fahrenheit_to_kelvin(30, True)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# Arguments are identified either by position or by keyword. All positional arguments come before all keyword arguments.\n",
"def the_count(one, two, three, four=4, five=5, six=6):\n",
" print(f\"One : {one}\")\n",
" print(f\"Two : {two}\")\n",
" print(f\"Three: {three}\")\n",
" print(f\"Four : {four}\")\n",
" print(f\"Five : {five}\")\n",
" print(f\"Six : {six}\")"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"ename": "TypeError",
"evalue": "the_count() got multiple values for argument 'four'",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-6-29bc3fadc58a>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mthe_count\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mfour\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0msix\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m30\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m: the_count() got multiple values for argument 'four'"
]
}
],
"source": [
"the_count(10,2,3,4,four=5,six=30)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Python 3 5/9 == 0.556\n",
"Python 3 5//9 == 0\n",
"Python 2 5/9 == 0\n"
]
}
],
"source": [
"# An aside on integer division, in python 2 integer division worked \n",
"# differently. It always rounded down to the nearest integer.\n",
"# The Python 2 behavior can still be accessed via double slash:\n",
"print(f\"Python 3 5/9 == {5/9:0.3}\")\n",
"print(f\"Python 3 5//9 == {5//9}\")\n",
"print(f\"Python 2 5/9 == {5//9}\")"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def kelvin_to_celsius(temperature):\n",
" return temperature - 273.15"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# Using functions we have defined in new functions works as expected:\n",
"def fahrenheit_to_celsius(temperature):\n",
" temp_in_kelvin = fahrenheit_to_kelvin(temperature)\n",
" return kelvin_to_celsius(temp_in_kelvin)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"-6.666666666666686"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"fahrenheit_to_celsius(20)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# So this all works, but \n",
"# clearly our functions aren't really specific to our notebook.\n",
"# They are quite general. This is why we will now look into moving\n",
"# them into their own file. Look at Modular temperatures.ipynb.\n",
"\n",
"\n",
"# A general note: Minimize state, avoid (nested) ifs.\n",
"# State makes it hard to understand what your code will do.\n",
"# Nested ifs will make it hard to reason about your code.\n",
"# Functions are nice to reason about if you don't use globals:\n",
"# You can see all their inputs and outputs."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Functional stuff\n",
"* you can work with functions as with variables\n",
"* you can pass functions as parameters\n",
"* you can define functions in functions and return them\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"f2c = fahrenheit_to_celsius"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"-6.666666666666686"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"f2c(20)"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def convert_many(function, temperatures):\n",
" result = list()\n",
" for t in temperatures:\n",
" result.append(function(t))\n",
" return result"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[-12.222222222222229, -5.0, 4.444444444444457, -23.333333333333343]\n",
"[-12.222222222222229, -5.0, 4.444444444444457, -23.333333333333343]\n"
]
}
],
"source": [
"temp_record_fahr = [10., 23., 40., -10.]\n",
"print(convert_many(f2c, temp_record_fahr))\n",
"print(list(map(f2c, temp_record_fahr)))"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# Why is map wrapped in list? Because by default it returns an iterator.\n",
"# The functions are not evaluated until needed:\n",
"\n",
"l = map(print, temp_record_fahr)"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"10.0\n"
]
}
],
"source": [
"next(l)"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"23.0\n"
]
}
],
"source": [
"next(l)"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"40.0\n"
]
}
],
"source": [
"next(l)"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"-10.0\n"
]
}
],
"source": [
"next(l)"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"collapsed": false
},
"outputs": [
{
"ename": "StopIteration",
"evalue": "",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mStopIteration\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-21-101c36968c6d>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ml\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;31mStopIteration\u001b[0m: "
]
}
],
"source": [
"next(l)"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"9000.0"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def make_me_a_temp_scale(abs_zero):\n",
" \n",
" def celsius_to_frank(temperature):\n",
" return temperature + 273.15 + abs_zero\n",
" \n",
" return celsius_to_frank\n",
"\n",
"celsius_to_frank_9000 = make_me_a_temp_scale(9000)\n",
"celsius_to_frank_9000(-273.15)\n",
"\n",
"# In this temperature scale, all temperatures are over 9000."
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# We can do both!\n",
"\n",
"def make_verbose(function):\n",
" def f2(arg):\n",
" res = function(arg)\n",
" print(f\"Function {function.__name__} returned {res}\")\n",
" return res\n",
" return f2\n",
"\n",
"# Of course this won't work if the function has more than one argument.\n",
"# To see how to make that work look up *args, and **kwargs."
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"f2c_verbose = make_verbose(f2c)"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Function fahrenheit_to_celsius returned -6.666666666666686\n"
]
},
{
"data": {
"text/plain": [
"-6.666666666666686"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"f2c_verbose(20)"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"t = (1,2,3)"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# There is a special syntax for such functions that modify functions,\n",
"# they are called function decorators in python and can be used like so:\n",
"\n",
"@make_verbose\n",
"def add_5(x):\n",
" return x+5"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Function add_5 returned 15\n"
]
},
{
"data": {
"text/plain": [
"15"
]
},
"execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"add_5(10)"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0\n",
"0\n",
"0\n",
"0\n",
"0\n"
]
}
],
"source": [
"# When are default parameters evaluated?\n",
"\n",
"i=0\n",
"\n",
"def print_i(x=i):\n",
" print(x)\n",
" \n",
"for j in range(5):\n",
" i += 1\n",
" print_i()"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0\n",
"1\n",