Rules for formatting¶
If you do not follow the indentation, the interpreter will print an error and the program will not be executed. To improve the readability of your code, it's a good idea to follow the following conventions:
- Decide how you will delimit the strings and follow one way throughout the code (you can swap the delimiters if the string contains
'
or"
so you don't have to use\'
or\"
). - Use lowercase names for variables and functions. Separate multi-word names with
_
(egprocess_data
). - Name the variables that will serve as constants in the program and that will not change at startup with capital letters separated by
_
(egDEFAULT_VALUE
). - Always separate the operators
=
,+=
,-=
,==
,!=
,<
,>
,<=
,>=
, is, in and Boolean conjunctionsor
,and
andnot
with spaces (egx < 10
instead ofx<10
). - Avoid inserting spaces immediately after/before
[/]
,(/)
, or{/}
(egx[10]
instead ofx[ 10 ]
,f(x)
instead off( x )
,{'x': 'y'}
instead of{ 'x' : 'y' }
).
More recommendations can be found here (in English).
Functions¶
You define functions in Python using the def
keyword followed by the function name and a list of parameters in ()
brackets separated by a comma. Function statements must be indented from the beginning of the line.
The following code defines a function print_value
with one parameter labeled value
. The function prints value
if it is not empty, otherwise it does nothing.
def print_value(value):
if value is None:
return # for an empty value, the 'return' command will terminate the function and the next print will not be executed
print(value)
# we call the defined function and pass its parameter value = 123
print_value(123)
The function print_value
does not return any value (its return value is always None
). If you want the function to return a value, use the return [value]
command, e.g.:
def sum_squares(data):
"""Computes sum of squares of values for the specified sequence."""
if len(data) == 0: # we return None for an empty list
return None # 'return' terminates the function and returns the value of the expression after the word 'return' as a return value
sumsq = 0
for x in data:
sumsq += x * x
return sumsq # at the end we return the sum of the squares of the list elements
# call the function with parameter data = [1, 2, 3] and assign its return value to variable x
x = sum_squares([1, 2, 3]) # x = 14
x
On the first line of the function sum_squares
is the so-called documentation string (docstring) used in Python to document code. Documentation strings are listed on the first line of a defined function before other statements, can be multiple lines, and are delimited by triple quotes """
. For functions, the string text is indented from the beginning of the line just like function statements. This convention is then used by various tools that eg automatically extract HTML documentation for all program functions.
The Python interpreter has a built-in help
function that prints a documentation string for the specified function or module name. For example after defining the sum_squares
function from the previous example, you can list its documentation with the following command:
help(sum_squares)
See here for more recommendations on writing documentation strings.
You can define preset values for function parameters and then omit the parameter when calling, e.g. the following function defines a default value for the opr
and none_value
parameters. All parameters with preset values must be listed after unset parameters.
def print_operation(x, y, opr="+", none_value="?"):
val_x = none_value if x is None else str(x) # none_value is assigned to val_x if x is None, otherwise str(x) is assigned
val_y = none_value if y is None else str(y) # similarly for y
print(val_x + " " + opr + " " + val_y)
Funkciu print_operation
možno volať nasledujúcimi spôsobmi:
print_operation(1, None, "*", "[none]") # print '1 * [none]'
print_operation(1, None, "*") # print '1 * ?' - we omitted none_value
print_operation(1, None) # prints '1 + ?' - we omitted opr and none_value
print_operation(x=1, opr="+", y=2) # you don't have to follow the order of the parameters and you can omit the optional parameters
# you can combine the methods, first the parameters are listed in order and then by name
print_operation(1, 2, none_value="[none]")
Note the exception to the formatting recommendations, for default parameter values it is not recommended to separate the parameter name and value from =
with a space.
In addition to listing parameters in order, you can set parameters by name when calling a function, e.g.:
print_operation(x=1, opr="+", y=2) # you don't have to follow the order of the parameters and you can omit the optional parameters
# you can combine the methods, first the parameters are listed in order and then by name
print_operation(1, 2, none_value="[none]")
You can also define a function with a variable number of parameters, e.g.:
def print_list(name, *args): # all parameters after the first will be passed to the function as tuple args
print(name + ": [" + ", ".join(args) + "]") # combine the args elements with the separator ', '
# the function can then be called with any number of parameters listed after name, e.g.
print_list("basket") # print 'basket: []'
print_list("basket", "apple", "orange") # print 'basket: [apple, orange]'
Functional programming - Lambda expressions¶
Functions are objects in Python that you can work with as any value, i.e. you can e.g. assign to a variable, sell them as a parameter when calling another function, or you can have a function that returns a function as a return value. Functional programming is particularly suitable for processing big data because it simplifies parallel and distributed computations.
The following filter function copies all elements from the items
list that meet the condition tested by the f
function into a new list. The filter
function expects the parameter f
to refer to a function with one parameter that returns a Boolean value.
def filter(items, f):
filtered = []
for x in items:
if f(x): # for each element x of the items list, we call the function referred to by parameter f
filtered.append(x) # if f returns True, we add x to the resulting list
return filtered # we return a list of values that passed testing with function f
We define a simple function with one parameter that tests whether the value of the parameter is non-zero.
def non_zero(x):
return x != 0 # return True if x != 0, otherwise False
# we can then pass the non_zero function as a parameter f to the filter function
a = [-1, 0, 1, 2, -3]
b = filter(a, non_zero) # = [-1, 1, 2, -3]
b
You can write simple functions with the 'return' command directly as a value using the so-called lambda expression. Lambda expression has the form: lambda
parameter list : return value, e.g. the following lambda expression defines the same function as non_zero(x)
:
test = lambda x: x != 0
# test is a variable in which the function defined by the lambda expression is stored
# calling the function assigned to the test variable is the same as if we defined a function with the name test
test(10) # = True
b = filter(a, test) # = [-1, 1, 2, -3]
# the abbreviated notation of the lambda expression is suitable, e.g. if you want to define a function directly where it is passed as a parameter:
b = filter(a, lambda x: x != 0)
b
You can also use a lambda expression if you want to program a function that returns a function as a return value, e.g. the following function test_interval(min, max)
returns a function that returns True
if the value of its parameter x
falls within
interval (min
, max
):
def test_interval(min, max):
return lambda x: min < x and x < max # 'return' returns the function defined by the lambda expression
# every time we call the function test_interval(min, max) with different parameters min, max, a new test function is created which
# can be used independently, e.g.:
test_interval(0, 10)(5) # = True - we created a new test function that we called directly with parameter 5
b = filter(a, test_interval(0, 3)) # = [1, 2]
b = filter(a, test_interval(-2, 2)) # = [-1, 0, 1]
Functions defined using a lambda expression are also referred to as anonymous. In addition to filter
, other useful functions include map
and reduce
.
# map returns a list whose values are calculated by applying the f function to the individual elements of the items list
def map(items, f):
mapped = []
for x in items:
mapped.append(f(x)) # we add the transformed value to the resulting list
return mapped
# returns a list of numbers raised to the power of 2
b = map(a, lambda x: x * x) # = [1, 0, 1, 4, 9]
b
The function reduce
expects as parameter opr
a function with two parameters, which it successively applies to the elements of the list and the result
of the previous opr
call, e.g. for the list [1,2,3]
function reduce
calculates the value:
opr(opr(opr(init, 1), 2), 3)
which can be written in infix form as ((init opr 1) opr 2) opr 3)
.
The init
value (with a default value of None
) is used when calling opr
with the first element of the list and is also the return value
reduce
for an empty list.
def reduce(items, opr, init=None):
result = init
for x in items:
result = opr(result, x)
return result
The reduce' function is particularly important in parallel programming, when the
opr' function is required to be associative, i.e. to for
all values a
, b
, c
were: a opr (b opr c) == (a opr b) opr c
. For the associative function, the following applies: a opr b opr c opr d == (a opr b) opr (c opr d)
, i.e. the parallel version of the reduce function can split the computation and compute the intermediate results (a opr b)
and (c opr d)
in parallel, and then combine them by calling opr
to the result. For the init
value, opr(init, x) = opr(x, init) = x
should further apply.
# you can use reduce to calculate e.g. the sum of the elements of the list (for the addition operation, init = 0)
sum = reduce(a, lambda x, y: x + y, 0) # calculation for [-1, 0, 1, 2, -3] corresponds to (((((0 + -1) + 0) + 1) + 2) + -3) = -1
# you can combine filter, map and reduce functions to calculate more complex functions
# e.g. for calculating the sum of squares of the positive elements of a:
b = filter(a, lambda x: x > 0) # we first filter out only positive elements
b = map(b, lambda x: x * x) # we calculate the powers
sum = reduce(b, lambda x, y: x + y, 0) # and we calculate their sum
# using a combination of map and reduce you can also calculate several statistics at once
# e.g. if we want to calculate the number and sum of the elements of the list a:
# first we transform a into a list of pairs, where the first component of the pair will always be 1 and the second element from a
b = map(a, lambda x: (1, x)) # = [(1, -1), (1, 0), (1, 1), (1, 2), (1, -3)]
# the parameters x and y of the opr function will be pairs, in the first component we will gradually add units and in the second the elements of the list and
# we set the pair (0, 0) as the initial value
count_sum = reduce(b, lambda x, y: (x[0] + y[0], x[1] + y[1]), (0, 0)) # the pair (number of elements, sum) is printed
count_sum
Tasks¶
Task 1.1¶
Write a function find_value(l, value)
that returns the position of the first occurrence of the list element l
with the given value value
, or -1 if no such element is found in the list.
Task 1.2¶
Write a function count_values(l)
that counts the number of times different values occurred in the list l
and returns a map with pairs (value, count).
Task 1.3¶
Modify the function find_values
from task 1.1 by adding a parameter default_index
with a default value of -1, whose value is returned if the value value
is not found in the list.
Task 1.4¶
Divide the solution of task 1.3 into simpler subtasks, for which program the necessary functions.