SCI-PROGRAMMING LECTURE 3_5
SCI-PROGRAMMING LECTURE 3_5
(CSC231)
CHAPTER 2: THE CORE PYTHON LANGUAGE I
J. D. Akinyemi (Ph.D.)
Department of Computer Science,
University of Ibadan, Ibadan, Nigeria
OUTLINE
5. Functions
1. Defining and Calling Functions
2. Default and Keyword Arguments
3. Scope
4. Passing Arguments to Functions
5. Recursive Functions
2
FUNCTIONS
• A Python function is a set of statements that are grouped together and named so
that they can be run more than once in a program.
• There are two main advantages to using functions.
• First, they enable code to be reused without having to be replicated in different parts of the
program.
• Second, they enable complex tasks to be broken into separate procedures, each implemented
by its own function – it is often much easier and more maintainable to code each procedure
individually than to code the entire task at once.
3
FUNCTIONS
Defining and Calling Functions
• The def statement defines a function, gives it a name and lists the arguments (if
any) that the function expects to receive when called.
• The function’s statements are written in an indented block following this def.
• If at any point during the execution of this statement block a return statement is
encountered, the specified values are returned to the caller.
4
FUNCTIONS
Defining and Calling Functions
• For example:
>>> def square(x):
... x_squared = x**2
... return x_squared
...
>>> number = 2
>>> number_squared = square(number)
>>> print(number , 'squared is', number_squared)
2 squared is 4
>>> print('8 squared is', square(8))
8 squared is 64
5
FUNCTIONS
Defining and Calling Functions
• The simple function named square takes a single argument, x.
• It calculates x**2 and returns this value to the caller.
• Once defined, it can be called any number of times.
• In the first example, the return value is assigned to the variable
number_squared;
• In the second example, it is fed straight into the print method for output to
the console.
6
FUNCTIONS
Defining and Calling Functions
• To return two or more values from a function, pack them into a tuple.
• For example, the following program defines a function to return both roots of the quadratic
equation ax2 + bx + c (assuming it has two real roots):
import math
def roots(a, b, c):
d = b**2 - 4*a*c
r1 = (-b + math.sqrt(d)) / 2 / a
r2 = (-b - math.sqrt(d)) / 2 / a
return r1, r2
print(roots(1., -1., -6.))
• When run, this program outputs, as expected:
(3.0, -2.0)
7
FUNCTIONS
Defining and Calling Functions
• It is not necessary for a function to explicitly return any object.
• Functions that fall off the end of their indented block without encountering
a return statement return Python’s special value, None.
• Function definitions can appear anywhere in a Python program, but a
function cannot be referenced before it is defined.
• Functions can even be nested, but a function defined inside another is not
(directly) accessible from outside that function.
8
FUNCTIONS
Defining and Calling Functions – Docstrings
• A function docstring is a string literal that occurs as the first statement of the function
definition.
• It should be written as a triple-quoted string on a single line if the function is simple, or
on multiple lines with an initial one-line summary for more detailed descriptions of
complex functions.
def roots(a, b, c):
’’’Return the roots of ax^2 + bx + c.’’’
d = b**2 - 4*a*c
9
FUNCTIONS
Defining and Calling Functions – Docstrings
• A docstring should provide details about how to use the function:
• which arguments to pass it
• which objects it returns
• but should not generally include details of the specific implementation of algorithms used by
the function (these are best explained in comments, preceded by #).
• Docstrings are also used to provide documentation for classes and modules
(see Sections 4.5 and 4.6.2).
10
FUNCTIONS
Defining and Calling Functions – Docstrings
11
FUNCTIONS
Default and Keyword Arguments
• In the previous example, the arguments have been passed to the function in
the order in which they are given in the function’s definition (these are
called positional arguments).
• It is also possible to pass the arguments in an arbitrary order by setting them
explicitly as keyword arguments:
roots(a=1., c=-6., b=-1.)
roots(b=-1., a=1., c=-6.)
12
FUNCTIONS
Default and Keyword Arguments
• If you mix nonkeyword (positional) and keyword arguments, the former
(positional) must come first.
• Otherwise Python won’t know to which variable the positional argument
corresponds
>>> roots(1., c=6., b=-1.) # OK
(3.0, -2.0)
>>> roots(b=-1., 1., -6.) # Oops!: which is a and which is c?
File "<stdin >", line 1
SyntaxError: non-keyword arg after keyword arg
13
FUNCTIONS
Default and Keyword Arguments
• Sometimes you want to define a function that takes an optional argument
• if the caller (i.e. where the function is called) doesn’t provide a value for this
argument, a default value is used.
• Default arguments are set in the function definition:
>>> def report_length(value , units='m'):
... return 'The length is {:.2f} {}'.format(value , units)
>>> report_length(33.136, 'ft')
'The length is 33.14 ft'
>>> report_length (10.1)
'The length is 10.10 m'
14
FUNCTIONS
Default and Keyword Arguments
• Default arguments are assigned when the Python interpreter first encounters the function
definition.
• This can lead to some unexpected results, particularly for mutable arguments.
>>> def func(alist = []):
... alist.append(7)
... return alist
...
>>> func()
[7]
>>> func()
[7, 7]
>>> func()
[7, 7, 7]
• The default argument to the function, func, here is an empty list, but it is the specific empty
list assigned when the function is defined.
• Therefore, each time func is called this specific list grows.
15
FUNCTIONS
Default and Keyword Arguments
16
FUNCTIONS
Scope
• A function can define and use its own variables.
• When it does so, those variables are local to that function: they are not
available outside the function.
• Conversely, variables assigned outside all function defs are global and are
available everywhere within the program file.
>>> def func():
... a = 5
... print(a, b)
...
>>> b = 6
>>> func()
5 6
17
FUNCTIONS
Scope
• The function func defines a variable a, but prints out both a and b.
• Because the variable b isn’t defined in the local scope of the function, Python
looks in the global scope, where it finds b = 6, so that is what is printed.
• It doesn’t matter that b hasn’t been defined when the function is defined, but
of course it must be before the function is called.
• What happens if a function defines a variable with the same name as a global
variable?
• In this case, within the function the local scope is searched first when
resolving variable names.
• So it is the object pointed to by the local variable name that is retrieved.
18
FUNCTIONS
Scope
>>> def func():
... a = 5
... print(a)
...
>>> a = 6
>>> func()
5
>>> print(a)
6
• Note that the local variable a exists only within the body of the function.
• It just happens to have the same name as the global variable a.
• It disappears after the function exits and it doesn’t overwrite the global a.
19
FUNCTIONS
Scope
• Python’s rules for resolving scope can be summarized as “LEGB”:
• first Local scope,
• then Enclosing scope (for nested functions),
• then Global scope,
• and finally Built-ins
• If you happen to give a variable the same name as a built-in function
(such as range or len), then that name resolves to your variable (in
local or global scope) and not to the original built-in.
• It is therefore generally not a good idea to name your variables after
built-ins.
20
FUNCTIONS
Scope: global and nonlocal keywords
• We have seen that it is possible to access variables defined in scopes other
than the local function’s.
• Is it possible to modify them (“rebind” them to new objects)?
• Consider the distinction between the behavior of the following functions:
>>> def func1():
... print(x) # OK, provided x is defined in global or enclosing scope
...
>>> def func2():
... x += 1 # not OK: can't modify x if it isn't local
...
>>> x = 4
>>> func1()
4
>>> func2()
UnboundLocalError: local variable 'x' referenced before assignment
21
FUNCTIONS
Scope: global and nonlocal keywords
• If you really do want to change variables that are defined outside the local scope,
you must first declare within the function body that this is your intention
• Use the keywords global (for variables in global scope) and nonlocal (for variables
in enclosing scope, for example, where one function is defined within another).
• In the previous case:
>>> def func2():
... global x
... x += 1 # OK now - Python knows we mean x in global scope
...
>>> x = 4
>>> func2() # no error
>>> x
5
22
FUNCTIONS
Scope: global and nonlocal keywords
• The function func2 really has changed the value of the variable x in
global scope.
• You should think carefully whether it is really necessary to use this
technique:
• would it be better to pass x as an argument and return its updated value from
the function?).
• Especially in longer programs, variable names in one scope that
change value (or even type!) within functions lead to confusing code,
behavior that is hard to predict and tricky bugs.
23
FUNCTIONS
Scope: global and nonlocal keywords
24
FUNCTIONS
Scope: global and nonlocal keywords
• The above program defines a function, inner_func, nested inside another,
outer_func.
• After these definitions, the execution proceeds as follows:
1. Global variables a = 6 and b = 7 are initialized.
2. outer_func is called:
a. outer_func defines a local variable, len = 2.
b. The values of a and b are printed; they don’t exist in local scope and there
isn’t any enclosing scope, so Python searches for and finds them in global
scope: their values (6 and 7) are output.
c. The value of local variable len (2) is printed.
d. inner_func is called:
25
FUNCTIONS
Scope: global and nonlocal keywords
(1) A local variable, a = 9 is defined.
(2) The value of this local variable is printed.
(3) The value of b is printed; b doesn’t exist in local scope so Python looks for it in enclosing scope,
that of outer_func. It isn’t found there either, so Python proceeds to look in global scope where it
is found: the value b = 7 is printed.
(4) The value of len is printed: len doesn’t exist in local scope, but it is in the enclosing scope
since len = 2 is defined in outer_func: its value is output.
3. After outer_func has finished execution, the values of a and b in global scope
are printed.
4. The value of len is printed. This is not defined in global scope, so Python searches
its own built-in names: len is the built-in function for determining the lengths of
sequences. This function is itself an object and it provides a short string description of
itself when printed.
26
FUNCTIONS
Scope: global and nonlocal keywords
• The output looks like this:
inside outer_func, a is 6 (id=232)
inside outer_func, b is 7 (id=264)
inside outer_func, len is 2 (id=104)
inside inner_func, a is 9 (id=328)
inside inner_func, b is 7 (id=264)
inside inner_func, len is 2 (id=104)
in global scope, a is 6 (id=232)
in global scope, b is 7 (id=264)
in global scope, len is <built-in function len> (id=977)
• Note that in this example outer_func has (perhaps unwisely) redefined (re-bound) the
name len to the integer object.
• This means that the original len built-in function is not available within this function (and
neither is it available within the enclosed function, inner_func).
27
FUNCTIONS
Passing Arguments to Functions
• A common question from new users of Python who come to it with a knowledge of
other computer languages is;
• “Are arguments to functions passed
• ‘by value’
• or ‘by reference?’ ”
• In other words,
• does the function make its own copy of the argument, leaving the caller’s copy unchanged,
• or does it receive a “pointer” to the location in memory of the argument, the contents of which
the function can change?
• The distinction is important for languages such as C, but does not fit well into the
Python name-object model.
28
FUNCTIONS
Passing Arguments to Functions
• Python function arguments are sometimes (not very helpfully) said to be
“references, passed by value”.
• Recall that everything in Python is an object, and the same objects may have
multiple identifiers (what we have been loosely calling “variables” up until
now).
• When a name is passed to a function, the “value” that is passed is, in fact, the
object it points to.
• Whether the function can change the object or not (from the point of view of
the caller) depends on whether the object is mutable or immutable.
29
FUNCTIONS
Passing Arguments to Functions
•A couple of examples should make this clearer.
•A simple function, func1, taking an integer argument, receives
a reference to that integer object, to which it attaches a local
name (which may or may not be the same as the global name).
•The function cannot change the integer object (which is immutable),
so any reassignment of the local name simply points to a new
object:
•the global name still points to the original integer object.
30
FUNCTIONS
Passing Arguments to Functions
>>> def func1(a):
... print('func1: a = {}, id = {}'.format(a, id(a)))
... a = 7 # reassigns local a to the integer 7
... print('func1: a = {}, id = {}'.format(a, id(a)))
...
>>> a = 3
>>> print('global: a = {}, id = {}'.format(a, id(a)))
global: a = 3, id = 4297242592
>>> func1(a)
func1: a = 3, id = 4297242592
func1: a = 7, id = 4297242720
>>> print('global: a = {}, id = {}'.format(a, id(a)))
global: a = 3, id = 4297242592
31
FUNCTIONS
Passing Arguments to Functions
• func1 therefore prints 3
• (inside the function, a is initially the local name for the original integer
object);
• it then prints 7 (this local name now points to a new integer object, with a
new id) – see Figure 2.5.
• After it returns, the global name a still points to the original 3.
• Now consider passing a mutable object, such as a list, to a function,
func2.
• This time, an assignment to the list changes the original object, and
these changes persist after the function call.
32
FUNCTIONS
Passing Arguments to Functions
>>> def func2(b):
... print('func2: b = {}, id = {}'.format(b, id(b)))
... b.append(7) # add an item to the list
... print('func2: b = {}, id = {}'.format(b, id(b)))
...
>>> c = [1, 2, 3]
>>> print('global: c = {}, id = {}'.format(c, id(c)))
global: c = [1, 2, 3], id = 4361122448
>>> func2(c)
func2: b = [1, 2, 3], id = 4361122448
func2: b = [1, 2, 3, 7], id = 4361122448
>>> print('global: c = {}, id = {}'.format(c, id(c)))
global: c = [1, 2, 3, 7], id = 4361122448
• Note that it doesn’t matter what name is given to the list by the function: this name points to the same
object, as you can see from its id.
• The relationship between the variable names and objects is illustrated in Figure 2.6.
• So, are Python arguments passed by value or by reference?
• The best answer is probably that arguments are passed by value, but that value is a reference to an object
(which can be mutable or immutable).
33
FUNCTIONS
Passing Arguments to Functions
34
FUNCTIONS
Passing Arguments to Functions
35
FUNCTIONS
Recursive Functions
• A function that can call itself is called a recursive
function.
• Recursion is not always necessary but can lead to elegant
algorithms in some situations.
• For example, one way to calculate the factorial of an
integer n ≥ 1 is to define the following recursive
function:
>>> def factorial(n):
... if n == 1:
... return 1
... return n * factorial(n - 1)
... 36
FUNCTIONS
Recursive Functions
• Here, a call to factorial(n) returns n times whatever is returned by the call
to factorial(n - 1), which returns n – 1 times the returned values of
factorial(n - 2) and so on until factorial(1) which is 1 by definition.
• That is, the algorithm makes use of the fact that n! = n · (n – 1)!
• Care should be taken in implementing such recursive algorithms to ensure
that they stop when some condition is met.
37
FUNCTIONS
Recursive Functions
38
FUNCTIONS
Recursive Functions
39
FUNCTIONS
Recursive Functions
40
FUNCTIONS: EXERCISES
41
FUNCTIONS: EXERCISES
42
FUNCTIONS: EXERCISES
43
FUNCTIONS: EXERCISES
44
REFERENCE TEXTS AND ONLINE RESOURCES
• Scientific Programming in Python
1. ** Christian Hill. Learn Scientific Programming with Python. 2nd edition, 2020.
Cambridge Press
2. Hans Petter Langtangen. A Primer on Scientific Prorgramming with Python. 4th edition
2014.
3. Robert Johansson. Numerical Python: Scientific Computing and Data Science
Applications with Numpy, Scipy and Matplotlib. 2nd edition. 2019. Apress.
• Python Basics
4. Finxter: https://app.finxter.com/learn/computer/science/ (Here, you will have fun while
you learn with python code puzzles)
5. Data Camp: https://www.datacamp.com/courses/intro-to-python-for-data-science
45