# Tutorial 2 -- Functions

### Functions

The keyword def introduces a function definition. It is followed by the function name and the parenthesized list of parameters. The statements that form the body of the function start at the next line, and must be indented.

In the vast majority of cases, a function is defined to return a value. The return statement returns with a value from a function. So the expected syntax of a function is

def MyFunction(args):
commands
return value

Here is an example of a function which returns the sequences of numbers ${a_k}$ up to integer $n$ such that $a_k = a_{k-1}+a_{k-2}$ starting with $a_0 = 0$ and $a_1 = 1$.

def fibonacci(n):
"""Returns a list containing the Fibonacci sequence up to integer n."""
fib_seq = []  # initialize empty list
a, b = 0, 1
while a < n:
fib_seq.append(a)    # this is equivalent to: fib_seq = fib_seq + [a]
a, b = b, a+b
return fib_seq

fib100 = fibonacci(100)    # call to the function: the result returned by the function
# is assigned to variable fib100
print(fib100)              # print the result

Python also supports simple anonymous functions through the lambda form. The executable body of the lambda function must be an expression and cannot be a statement.

import math

specialfunc = lambda x: math.exp(x) - x
print(specialfunc(1.0))

The values given to the arguments of a function must be compatible with the function itself. Otherwise you will get error messages and your code will stop running.

return a+b

The last call will lead to an error and your code will stop working. TypeError: unsupported operand type(s) for +: 'int' and 'str'

In Python, functions can be called recursively, that is, a function can call itself:

def fact(n):
"""Returns the factorial of integer n."""
if n <= 1:
return 1
else:
return n * fact(n - 1)
N=10
print(N,'! = ',fact(N))

Here is another example of a recursive call:

a = int(input("Enter a : "))
b = int(input("Enter b : "))

def gcd(a, b):
"""Returns the gcd of a and b"""
if a == 0 :
return b
return gcd(b%a, a)

print("gcd(", a , "," , b, ") = ", gcd(a, b))

Remember that, in python, all created variables are local, if not otherwise declared. The use of global variables is generally bad practice and should be avoided. So when you define a variable inside a function definition, it is local to this function by default. This means that any change to this variable in the body of the function will have no effect on other variables outside of the function, even if they have the same name. Here is a simple example to demonstrate this feature:

x= 9.81              # This variable “x” in the main body of the script is a float

def function1():
x=1              # This variable "x" is an integer: it is local to function1
print('Function 1, x=',x)

def function2():
x='Help!'         # This variable “x” is a string: it is local to function2
print('Function 2, x=',x)

print('In the main body, x=',x)
function1()
function2()

On output, you will get

In the main body, x= 9.81
Function 1, x= 1
Function 2, x= Help!

### Global vs Local Variables

Global variables are visible within a function. So variables declared outside the function can be referenced within the function:

def print_x():
print("Within the function, x=", x)

x = 11
print_x()
print("After the call to the function, x=", x)

On output, you will get:

Within the function, x= 11
After the call to the function, x= 11

Or try this:

x = 5

return x + y

addx(10)  # this will give 15

What happens if we access a global value within a function, and then assign a new value to it? This would imply that we create a local variable with the same name. We would have then this variable both as a global and a local variable in the same scope, i.e. the body of the function. Python fortunately will not allow this, and it will throw an error, as can be seen in the following example:

def f():
print(s) # here we print global variable s
s = "I love UPenn!" # now we modify the value of s locally
print(s)

s = "I love UD!"
f()

On output, you will get an error:

UnboundLocalError: local variable 's' referenced before assignment

Local variables of functions cannot be accessed from outside, once the function call has been made: here is an illustration

def f():
s = "Thanks!"
print(s)

f()
print(s)

You will get this error

NameError: name 's' is not defined

If you want to modify a “global” variable within a function, then you will have to declare it global in the function.

def f():
global s
print(s)
s = "Go Blue Hens!"
print(s)

s = "Go Quakers!"
f()
print(s)

On output, you will get:

Go Quakers!
Go Blue Hens!
Go Blue Hens!