At any part of a Python code you can define a function with the
def keyword. A function is a block of code which only runs when it is called.
Mind the code block (identation) and colon.
Example: A function that prints a message:
def hi(): print("Hi there!")
Example: An empty function (useful when writing a big program, you know that you need this function but you will write the code of it later):
def function_name(n): # PEP-8: use small letters and _ between words pass function_name(5)
The following is a function definition, a recipe which tells what to do if the function is called.
def square(l): """Calculate the square of the elements of a list """ new_l =  for i in l: new_l.append(i*i) return new_l
def keyword there is the name of the function (how we would like to call it).
After that the parameter(s), comma separated, in parenthesis. After the colon there is the function body, that part is executed when the function is called.
Inside the function body there is a
return keyword which tells the function to stop and the result of the function will be the value after the word
return is optional.)
To call a function write its name and the necessary parameters in a parenthesis (in the example: a single parameter which is a list):
square([4, 3, 5])
[16, 9, 25]
One can call the function to operate on a previously defined variable.
numbers = [5, 1, 8] squared_numbers = square(numbers) print(numbers, squared_numbers)
[5, 1, 8] [25, 1, 64]
If the function is correct then the result is the list of squared numbers and nothing unexpected happens, but look what happens in the following solution.
def square2(l): i = 0 while i < len(l): l[i] = l[i] ** 2 i += 1 return l numbers = [5, 1, 8] squared_numbers = square2(numbers) print(numbers, squared_numbers)
[25, 1, 64] [25, 1, 64]
The function modified its parameter, the original list, and also returned the modified list.
We will go into details on this issue later in the semester, but for now: write functions which do not modify their parameters.
Parameters are listed in a comma separated list.
Example: Calculate the dot product of two vectors which are represented by lists:
def dot_product(v1, v2): result = 0 for i in range(len(v1)): result += v1[i] * v2[i] return result
dot_product([-2, 3, 5], [1, 5, -2])
Parameters can be any objects with any type. A function can have any given number of parameters, even 0, but nevertheless the parenthesis is mandatory.
A function (at first glance) is called in this way:
A method looks like this:
l = [5, 2, 4] l.sort() print(l)
[2, 4, 5]
sort is a built in method of lists (a number itself cannot be sorted).
For example there is a similar function called
l = [5, 2, 4] new_l = sorted(l) print(l, new_l)
[5, 2, 4] [2, 4, 5]
sorted does not modify the list itself, but returns a new, sorted list.
This is a common practice with methods and functions, but not a law.
Later we will learn how to write methods.
return exits the function without processing any further parts of the function body. One can take advantage of this behaviour:
def is_a_prime(n): """n: is a positive integer return: True if n is prime, otherwise False """ divisor = 2 while divisor**2 <= n: if n % divisor == 0: return False divisor += 1 return True
If the function arrives at a true divisor then returns immediately because there is no need to look further.
True value is returned only if none of the numbers were non-trivial divisors.
break exits a loop (only a loop and only one of them), and
return exits a function (only a function and only one of them, but breaks any loop in that function).
return None to represent "no value is returned".
If a function has no
return command then the result will be
For example is you forgot to write
return or you wrote it to the wrong place.
.sort() method results
None, but it sorts the list as a side-effect.
l = [3, 2, 1, 4] l2 = l.sort() print(l2, l)
None [1, 2, 3, 4]
def hi2(): return "Hi everyone!"
a = hi() b = hi2() print("Returned values:", a, b)
Hi there! Returned values: None Hi everyone!
You can call any function (once defined) inside other functions as well. It is not only possible but encouraged!
Best if you write no more than 4-5 line functions and put those functions together to solve bigger problem. This way your functions will be shorter and harder to make mistakes. The goal and the mechanism of the function should be clear by reading its name and its parameters.
Exercise: Write a function which has one parameter, a list, and finds the smallest and greatest elements in the list. The output is a list with replace the extremal values with a 0 value!
How to solve the task?
For example in this task the subtasks are:
Lets solve these
def minimum(l): min_elem = float("inf") for e in l: if e < min_elem: min_elem = e return min_elem def maximum(l): max_elem = -float("inf") for e in l: if e > max_elem: max_elem = e return max_elem def erase(l, elem): # shallow copy of l (copy element by element, that is with slice l) # newl is a new object newl = l[:] # the argument l will not be changed for i in range(len(newl)): if newl[i] == elem: newl[i] = 0 return newl
Now you have everything to write the main function:
def min_max_erase(l): minelem = minimum(l) maxelem = maximum(l) newl = erase(l, minelem) newl = erase(newl, maxelem) return newl
min_max_erase([2, 3, 1, 4, 6, 2, 9, 3, 1, 3, 1, 9, 3, 9])
[2, 3, 0, 4, 6, 2, 0, 3, 0, 3, 0, 0, 3, 0]
min_max_erase([1, 1, 1, 2])
[0, 0, 0, 0]
A shorter solution for the last part (last three lines of the main function):
return erase(erase(l, minelem), maxelem)
You can solve this in one big function:
def min_max_erase2(l): min_elem = float("inf") for e in l: if e < min_elem: min_elem = e max_elem = -float("inf") for e in l: if e > max_elem: max_elem = e newl = l[:] for i in range(len(newl)): if newl[i] == min_elem: newl[i] = 0 for i in range(len(newl)): if newl[i] == max_elem: newl[i] = 0 return newl
min_max_erase2([2, 3, 1, 4, 6, 2, 9, 3, 1, 3, 1, 9, 3, 9])
[2, 3, 0, 4, 6, 2, 0, 3, 0, 3, 0, 0, 3, 0]
The first solution is
The second solution works, too.
Write a function which sorts a given list (a single parameter) but write it on your own, without using the builtin
sort method or the
def bubble(l): newl = l[:] for i in range(len(newl) - 1): for j in range(len(newl) - i - 1): if newl[j] > newl[j + 1]: newl[j], newl[j+1] = newl[j+1], newl[j] # temp = newl[j]; # these three lines # newl[j] = newl[j + 1]; # are equivalent to # newl[j + 1] = temp # the previous one return newl
bubble([2, 3, 1, 4, 6, 2, 9, 3, 1, 3, 1, 9, 3, 9])
[1, 1, 1, 2, 2, 3, 3, 3, 3, 4, 6, 9, 9, 9]
bubble(list(range(10, 0, -1)))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Lets print the actual state during the algorithm:
def bubble_print(l): newl = l[:] for i in range(len(newl) - 1): for j in range(len(newl) - i - 1): print(newl) # print here if newl[j] > newl[j + 1]: newl[j], newl[j+1] = newl[j+1], newl[j] return newl
bubble_print(list(range(5, 0, -1)))
[5, 4, 3, 2, 1] [4, 5, 3, 2, 1] [4, 3, 5, 2, 1] [4, 3, 2, 5, 1] [4, 3, 2, 1, 5] [3, 4, 2, 1, 5] [3, 2, 4, 1, 5] [3, 2, 1, 4, 5] [2, 3, 1, 4, 5] [2, 1, 3, 4, 5]
[1, 2, 3, 4, 5]
There are more sophisticated (and faster) algorithms, you will learn those in the Theory of Algorithms class.
First subtask is finding a minimum.
def armin(l): min_place = 0 min_elem = l for i in range(len(l)): if l[i] < min_elem: min_elem = l[i] min_place = i return min_place
armin([3, 2, 100, -1, 1])
Then solve the whole task.
def sort_min(l): newl = l[:] for i in range(len(newl)-1): j = armin(newl[i:]) newl[i], newl[i + j] = newl[i + j], newl[i] return newl
sort_min([3, 2, 100, -1, 1])
[-1, 1, 2, 3, 100]