Functions

In [1]:
def multiply2(x):
    """Multiply by two"""
    return x * 2

def divide(numerator, denominator):
    pass
In [2]:
multiply2(4)
Out[2]:
8

A function return alwais something, even if is not specify

In [3]:
divide(1, 0) is None
Out[3]:
True
In [4]:
def func0(x):
    return

def func1(x):
    return None

def func2(x):
    y = x**x

x = 2
print func0(x) == func1(x) == func2(x) == None
True

Define a simple function, again, and use it a parameter of another function!

In [5]:
def add(a, b):
    return a + b

def fun(a, b, func):
    return func(a, b)

fun('c','d', add)
Out[5]:
'cd'

Exercise 8

Write the divide function in order to manage zero division and not numeric values [5 minutes]

In [9]:
def divide(numerator, denominator):
    pass

Use the code below as a test:

In [12]:
divide(4, 2)  # return 2
2
In [11]:
divide(4, 0)  # print Divide a number with 0 is not a valid operation!
Divide a number with 0 is not a valid operation!
In [10]:
divide(1,'a') # print Not a valid parameter!
Not a valid parameter!

One possible solution is:

In [ ]:
 

Use a number variable of parameters.

In [13]:
def summatory(*args):
    result = 0
    for num in args:
        result += num
    return result

summatory(1, 2, 3, 4, 5)
Out[13]:
15
In [14]:
summatory(*range(1, 6))
Out[14]:
15
In [15]:
range(1, 6)  # start, stop, step
Out[15]:
[1, 2, 3, 4, 5]

Define a custom function with some default parameters

In [16]:
def multiply(number, mult=2):
    return number * mult
In [17]:
multiply(4)
Out[17]:
8
In [18]:
multiply('a')
Out[18]:
'aa'
In [19]:
multiply('a', 6)
Out[19]:
'aaaaaa'
In [20]:
multiply('a', mult=8)
Out[20]:
'aaaaaaaa'

Undefined number of key-value parameter

In [21]:
def contact(**kargs):
    for key, val in kargs.items():
        print "%s => %r" % (key, val)
In [22]:
contact(pietro=33312388, john=2345678)
pietro => 33312388
john => 2345678

Exercise 9

Define a function that given a number variable of parameters and key-value parameters, print: [5 minutes]

arg: 1
arg: 2
arg: a
arg: [1, 2, 3]
karg[other]: 'ciao'
karg[key]: 'value'
In [25]:
args_kargs(1,2,'a',[1,2,3], key='value', other='ciao')
arg: 1
arg: 2
arg: 'a'
arg: [1, 2, 3]
karg[other]: 'ciao'
karg[key]: 'value'

Solution

In [24]:
def args_kargs(*args, **kargs):
    pass

Function containig other functions

In [ ]:
def summ_mult(list_of_tuple):
    # define a new function inside
    def summ(tupl):
        res = 0
        for x in tupl:
            res += x
        return res
    
    result = 1
    for el in list_of_tuple:
        result *= summ(el)
    return result
In [ ]:
summ_mult([(1,2,3), (4,5,6)])

A function could return something using the command return or inside a cycle using yield:

In [ ]:
def use_yield(*args):
    for a in args:
        yield a * 2
In [ ]:
use_yield(1, 2, 3, 4, 'a')

Using yield transform the function in to a generator, every time that we ask to the generator the next value, the generator compute the next and it will return the resulting object, in this way we don't need to reserve the memory for the list, therefore consume less memory and is generally faster.

In [ ]:
[i for i in use_yield(1, 2, 3, 4, 'a')]
In [ ]:
usy = use_yield(1, 2, 3, 4, 'a')
In [ ]:
usy.next()

Useful functions in the python standard library: http://docs.python.org/2/library/functions.html#built-in-functions

In [ ]:
str(2.8)
In [ ]:
range(1,10,2)
In [ ]:
xrange(1,10,2)
In [ ]:
[i for i in xrange(1,10,2)]
In [ ]:
len([1, 2, 3, 4])  # return the lenght of an object
In [ ]:
names = ['one', 'two', 'three', 'four', 'five']
values = [1, 2, 3, 4, 5]
zip(names, values)  # return a list of pairs
In [ ]:
for name, value in zip(names, values):
    print '%5s = %d' % (name, value)
In [ ]:
dir('a')  # return all the attributes of an object

Decorator

Decorator are object that are called to do something before and after a method or a function.

In [ ]:
VERBOSE = True

def verbose(func):
    def wrapper(*args):
        if VERBOSE:
            print "Before to execute: %s" % func.func_name
        result = func(*args)
        if VERBOSE:
            print "After the execution: %s" % func.func_name
        return result
    return wrapper

We can test our decorator with:

In [ ]:
verbose(multiply2)(1)

Usually decorators are used with "@", and are very useful when you want to avoid to repeat the same operations to different functions.

In [ ]:
VERBOSE = True # try to change

@verbose
def mult(*args):
    result = args[0]
    for i in args[1:]:
        result *= i
    return result

mult(1, 2, 3, 4)

Write another decorator that print the execution time of a function.

In [ ]:
import time

def timeit(function):
    def timed(*args, **kargs):
        # do something before
        time_start = time.time()
        # execute the function
        result = function(*args, **kargs)
        # do something after
        time_end = time.time()
        print 'name=%r (args=%r, kargs=%r) processing time=%10.8f sec' % (function.__name__, args, kargs, time_end - time_start)
        return result

    return timed
In [ ]:
timeit(multiply2)(2)
In [ ]:
@timeit
def mult(*args):
    result = 1
    for a in args:
        result *= a
    return result
In [ ]:
mult(1,2,3,4)