Functions

Defining new functions

  • As in most programming languages, in Python you can define your own functions.
  • You give your new function a name, it may or may not take arguments and it can have a single return value.

In [20]:
a= "I'm a string"

def fibonnaci(n):
    """Will give you back the nth fibonnaci number, starting at 0"""
    # Can put in triple-quoted string here as a doc-string which is used in help()
    # Functions are indented
    a, b = 0, 1
    for i in range(n-1):
        a, b = b, a+b
    return a

help(fibonnaci)
print(fibonnaci(6))  # Call functions in the obvious way
print(a)  # 'a' is unchanged by its doppleganger inside the function


Help on function fibonnaci in module __main__:

fibonnaci(n)
    Will give you back the nth fibonnaci number, starting at 0

5
I'm a string
  • Functions have their own scope for variables. If you try to use a variable inside a function, Python will first lookup if it was defined inside this function. Then if it can't find it it will move outwards in scope until it finds it or fails (raising an execption).
  • Note in the above case that defining a new 'a' variable inside the function didn't overwrite the variable 'a' defined outside first. Instead it created a new 'a' variable in the function's scope, which was used from then on until the function returned.
  • Variables defined outside the scope of any function are called global variables. Because Python will search outer scopes for a variable if you use it in a function without first assigning it, it is best practice not to assign any global variables unless they are constants. People will often use a main() function for the top logic of their script to avoid defining any global variables (see scripts directory).
  • You can also explicitly allow a global variable to be assigned to from inside a function by using the 'global' keyword. Python 3 has an additional keyword, 'nonlocal', which behaves similarly but we won't address here.

In [1]:
a = 10

def global_change():
    global a  # Have to call 'global a' to gain access to 'a' so we can reassign it.
    a = 20

def print_global():
    print(a)   # Can read access variables outside of function without calling global

global_change()
print_global()


20
  • All function arguments are passed by reference not by value.
  • In C when you use a variable to pass a value into a function, an implicit copy is made of that variable and the variable inside the function is completely new but with the same value.
  • In Python a variable is passed by reference into a function, meaning that the variable used inside the function is the same object as the one passed in. This means that if you pass a mutable object into the function you can change its value and the variable outside the function will reflect this change. Beware that non-mutable types like integers/floats cannot have their values changed without creating a new object anyway.

In [12]:
def change_value(x):
    print(id(x), end=" ")  # Is it the same object?
    x.pop()  # remove the last element

a = list(range(6))  #Construct list
print(id(a),a)  # Find out its id and contents
change_value(a)  #Use function to change it in place with no copying
print(a)


3058952044 [0, 1, 2, 3, 4, 5]
3058952044 [0, 1, 2, 3, 4]
  • Every time you specify a name for an argument of a function you are allowed to simply use that keyword when you're calling the function. This means that you can give the function arguments out of order when calling the function.
  • Python functions also support default values for function arguments which are specified inside the function definition.
  • Using both of these features gives you great flexibility about the arguments you need to pass to a function.

In [22]:
def many_arguments(first_name, surname, country, dob="1979 01 01"):
    return [first_name, surname, country, dob]
# Your default arguments must come last in the function definition

print(many_arguments("David", "Dossett", "UK", "1987 02 20"))
print(many_arguments("David", country="UK", surname="Dossett"))


['David', 'Dossett', 'UK', '1987 02 20']
['David', 'Dossett', 'UK', '1979 01 01']
  • Functions are first class objects in Python and as such they can be bound to variable names and passed around like any other object.
  • However, this means that when they are defined their default arguments are also defined in memory, and will be the same objects whenever they are used. This can cause confusion with mutable types.

In [4]:
# BE CAREFUL ABOUT USING MUTABLE TYPES AS DEFAULT ARGUMENTS!!!
def mutable_weird(x=[]):
    x.append(1)
    return x

print(mutable_weird())
print(mutable_weird())
print(mutable_weird())
print(mutable_weird([0]))  #Passing in a list instead of using the default

# Can assign variable names to a function object
func = mutable_weird  # Note the absence of brackets
print(func)
print(func())


[1]
[1, 1]
[1, 1, 1]
[0, 1]
<function mutable_weird at 0xb67005cc>
[1, 1, 1, 1]
  • Functions can also be defined within each other (nested) and Python supports recursive functions, where a function can call itself.
  • You can also pass a function as an argument to another function. We saw an example in the sorted() function while discussing looping, which can take arguments of the form sorted(myvar, key=myfunc).
  • A useful way to quickly create functions to pass into these types of functions is to use lambda functions. These are anonymous functions (they don't get a function name) which can be created almost anywhere in your code without requiring a def keyword.

In [10]:
# lambda syntax;  lambda arguments: expression
odds = list(filter( lambda x: x%2, range(10)))
print(odds)
# Filter() takes a function and an iterable as arguments and returns an iterator
# that can be used to create the list. Only elements that return True from the 
# passed function will be used in the final list.


[1, 3, 5, 7, 9]

In [11]:
def f(x, y):
    return x+y

g = lambda x, y: x+y  # Equivalent lambda function bound to a variable

print(f(4,6), g(4,6))  # Called in the same way


10 10
  • Lambda functions implicitly return the value of their expression.
  • Lambda functions can be used to solve similar problems as list comprehensions. There are several builtin functions that take functions as arguments and are useful to know how to use, sorted(), map(), filter() and reduce().
  • Through these properties Python supports Functional Programming but does not enforce its use, which is an advanced topic we won't discuss.