When people talk about "Python" they usually mean four things. (1) The python language itself, (2) the python software that we use e.g. to execute a script python myscript.py
, (3) the python functions and modules that come automatically with Python, (4) the external packages that we can install to achieve specific tasks (e.g working with matrices, images etc.)
We are going to now briefly review the essential python language needed for this course. This includes variables, some containers, flow control, functions and modules. Most of the more complex operations we are going to use come from external packages that we are going to explore in detail later. Be aware that this is not an exhaustive Python description, but really the bare minimum needed for this course. If you decide to seriously use Python for image processing, it's probably good to once read more in detail about Python basics. We can recommend for example Jake VanderPlas' book A Whirlwind Tour of Python entirely available as interactive notebooks.
Like any other programming language, Python works with variables: as we do in algebra, instead of directly working with numbers we work with symbols that can take different values. So we can just write:
a = 3
And then re-use the variable a
in our program, or here in our notebook. For example:
3*a
9
Unlike e.g. C++ where you have to say in advance what type your variable has (integer, float, string etc.) Python does that for you by interpreting your intensions. This is useful and makes writing code much simpler, but remember that this can be the source of bugs. Let's create a few variables:
a = 3
b = 1.2
c = 'my text'
d = True
We can use the type
function to get the variable type:
type(a)
int
type(b)
float
type(c)
str
type(d)
bool
Note that if we start mixing variables, Python is also going to interpret the resulting type for us:
e = a * b
type(e)
float
As already seen above you can do simple mathematics with variables as signs such as +
and *
are understood by Python.
In addition to those, you can also perform logical operations with boolean variables. A boolean can be True
or False
and is typically generated via a comparison:
a > b
True
my_boolean = a > b
my_boolean
True
my_boolean2 = e == a
my_boolean2
False
my_boolean and my_boolean2
False
my_boolean or my_boolean2
True
We will see that booleans are helpful when dealing with image masks.
Python functions are just like mathematical functions $y = f(x)$, they have a name f
, take an input x
and give an output y
. We have just seen an example of such a function above with type()
. Function definitions can come from different places:
These functions are directly implemented in Python and you don't have to do anything special to use them. You can find a list here. For example, you can take an absolute value:
a = -3
b = abs(a)
b
3
All additional functions come from modules. Those are separate software packages logically organised by domains (handling files, doing maths etc.) that one can import in a program. There are two types of modules: some are native to Python (no installation necessary) and some are distributed externally (and need to be installed using e.g. pip or conda).
Let's imagine we want to know the content of folders e.g. to execute some program on all images within a folder. We can use for that the os
package. We import it using:
import os
Then all functions of the os
module are available using the dot notations:
mypath = os.getcwd()
mypath
'/Users/gw18g940/OneDrive - Universitaet Bern/Courses/BiaPy/BIAPy'
The getcwd()
function gives us the current path. In this particular case, there is no input, just an output in the form of a path string.
Some functions that logically belong together are also sometimes grouped together into sub-modules, that one can call again using the "dot noation". For example we can do all sorts of operations on path strings using the functions grouped in the os.path
sub-module:
os.path.basename(mypath)
'BIAPy'
The function above takes a path as input and returns the last part of the path.
External packages that we installed ourselves are imported in the exact same way. For example we are going to use a lot Numpy, which allows us to do matrix computations:
import numpy
Note that to abbreviate code, it is very common to abbreviate the name of imported packages. This can be done like this:
import numpy as np
Note that in some cases, sub-modules need to be imported separately. For example the import/export module of the image processing package scikit-image:
import skimage.io
If is very easy to create a function in Python. For example the minimum you need to define a parabola function ($f(x) = x^2$) is:
def parabola(x):
y = x**2
def
indicates that we define a parabola. The parabola
name is chosen by us. Like in algebra we have a parenthesis with variables, here x
. Then we have a column :
and everything that follows it and which is indented belongs to the function body.
We can then use the function name and pick a parameter value of our choice:
result = parabola(3)
result is None
True
We see that the last problem we have is that the function doesn't return any value. It does the computation but keeps it hidden from us. The solution to this is to explicitly say what we want to return
:
def parabola(x):
y = x**2
return y
result = parabola(3)
result
9
We can naturally have more that one parameter to pass to the function (e.g. $f(x,a, b) = a * x^2+b$). Parameters can also have a default value as shown here, where the parameter b == 2
:
def parabola2(x, a, b=2):
y = a * x**2 + b
return y
parabola2(3,0)
2
The default can of course be overriden by setting an explicit value:
parabola2(3,0,10)
10
mylist = [1,6,10,20]
A list can contain any type of variable, and you can even mix them, even though that happends rarely:
mylist_mix = [1, 6.8, 'this is my text',[9,10,3]]
mylist_mix
[1, 6.8, 'this is my text', [9, 10, 3]]
Dictionaries are pairs sets of pairs of definitions and values, like an actual dictionary. They are creted in the following way:
mydict = {'name': 'apple', 'weight': 100, 'diameter': 10}
mydict
{'name': 'apple', 'weight': 100, 'diameter': 10}
Dictionaries are useful to reference properties of objects e.g. the geometric properties of segmented objects in an image.
Elements of both lists and dictionaries can be accessed using square bracktets []
. List elements are selectable via a positional index (starting at 0), e.g. if you want to access the third element you do:
mylist_mix[2]
'this is my text'
And dictionary elements can be accessed via the definitions or keys:
mydict['weight']
100
You can also modify an element in the same way:
mylist_mix[2] = 'new text'
mylist_mix
[1, 6.8, 'new text', [9, 10, 3]]
mydict['weight'] = 200
mydict
{'name': 'apple', 'weight': 200, 'diameter': 10}
Beware that simple copies of lists and dictionaries are not independent copies!:
mylist_mix2 = mylist_mix
mylist_mix[0] = 'I changed this'
mylist_mix
['I changed this', 6.8, 'new text', [9, 10, 3]]
mylist_mix2
['I changed this', 6.8, 'new text', [9, 10, 3]]
To create a true independent copy you can use the copy()
methods on your list:
mylist_mix2 = mylist_mix.copy()
mylist_mix[0] = 'again'
mylist_mix
['again', 6.8, 'new text', [9, 10, 3]]
mylist_mix2
['I changed this', 6.8, 'new text', [9, 10, 3]]
If you are familiar with programming you probably know about object oriented programming. If not, just know that in programming an object can have "features" (attributes) and "functionalities" (methods). Since every Python variable is an object, it means that each variable, on top of its value, can contain additional information. Let's look at a practical example. We define an complex number:
imaginary = 3 + 5j
type(imaginary)
complex
Even this simple variable has both attributes and methods. For example we can recover the real and imaginary parts of the number through the attributes real
and img
. This is done with the dot-notation:
imaginary.real
3.0
imaginary.imag
5.0
Methods are accessed with the same dot-notation. However since they are actual functions, we need to use parentheses. For example, we can create a string variable and then use it's count()
method to count the number of occurrences of a given letter:
mystring = 'this is my string'
mycounts = mystring.count('i')
print(mystring)
mycounts
this is my string
3
Like any other programming language, Python posesses flow control mechanisms to repeat operations (for, while) or separate different cases (if, else).
The main specificities of Python are:
:
mylist = [1,2,3,4,5,6,7,8]
for x in mylist:
y = x**2
print(y)
1 4 9 16 25 36 49 64
If we un-indent the print statement it doesn't belong to the loop anymore and is executed after it:
for x in mylist:
y = x**2
print(y)
64
If statements follow the same rules:
a = 3
b = 5
if a > b:
print('yes')
else:
print('no')
no
The last point which brings sometimes confusion is the in what context variables are available or in other words their scope. Let's imagine the following code:
a = 1
def myfun():
a = 2
print(f'value in myfun: {a}')
def myfun2():
print(f'value in myfun2: {a}')
print(f'initial defintion: {a}')
myfun()
myfun2()
print(f'final defintion: {a}')
initial defintion: 1 value in myfun: 2 value in myfun2: 1 final defintion: 1
So first we define a = 1
. Then we define two functions. In one of them we redefine the same variable as a = 2
and print it, in the second one, we use it without redefining it. From the print statements we see that:
a
defined in the notebook (or script) is unaffected by what happens in functions.Finally, let's see what happens at the level of modules. In the course_functions.py module, we defined the same functions as myfun
and myfun2
. We load them first and then call them:
from course_functions import myfun_in_script, myfun_in_script2
myfun_in_script()
final defintion: 2
myfun_in_script2()
--------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-57-e3287a08bcaa> in <module> ----> 1 myfun_in_script2() ~/OneDrive - Universitaet Bern/Courses/BiaPy/BIAPy/course_functions.py in myfun_in_script2() 45 46 def myfun_in_script2(): ---> 47 print(f'final defintion: {a}') NameError: name 'a' is not defined
We see now that even though we defined the variable a
in this notebook, the function myfun_in_script2
in the module is not aware of it. Thus a
is only global at the notebook level.
There are other potential cases not mentioned here. For a more in-depth look at this topic you can read more in this great notebook from Sebastian Raschka (you can also check out his great book on Machine Learning.