April, 2022 - François HU
Master of Science - EPITA
This lecture is available here: https://curiousml.github.io/
Python’s standard library is very extensive, it contains built-in modules (written in C). If it is your first time using python and jupyter notebook, pleaser refer to the following pages:
In a nutshell, this notebook gives a quick look at some built-in types and functions.
Note that :
#
. When we run the script, what comes after #
is ignored.help()
or ?
.# let us check the description of the function `print`
help(print)
Help on built-in function print in module builtins: print(...) print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False) Prints the values to a stream, or to sys.stdout by default. Optional keyword arguments: file: a file-like object (stream); defaults to the current sys.stdout. sep: string inserted between values, default a space. end: string appended after the last value, default a newline. flush: whether to forcibly flush the stream.
As in other languages, variables hold values. In Python, variables do not require forward declaration, all you need to do is provide a variable name and assign it some value. The present section describes some classical variables in python: numeric, boolean, text sequence (aka string) variables. This section also describes some classical container variables:
Note that these variables are built into the interpreter.
Let us assign values to variable names and print them (as well as their type)
# integer
a = 1
# float
b = 1.2
# complex
c = 1+2j
print(a)
type(a)
1
int
print(b)
type(b)
1.2
float
print(c)
type(c)
(1+2j)
complex
A more compact way to assign values is:
a, b, c = 1, 1.2, 1+2j
print(a, b, c)
1 1.2 (1+2j)
It's also possible to swap variables in a more compact way:
x, y = 1, 2
print(x, y)
1 2
u, v = -1, 0
x, y = u, v
print(x, y)
-1 0
We can create a boolean by:
True
and False
type(a == 1)
bool
type(True)
bool
print(a == 2)
print(a != 2)
print(a == 1)
print(a > 1)
print(a < 1)
print(a <= 1)
print(a >= 1)
print(True)
print(False)
False True True False False True True True False
using and
and or
condtions:
print(True or False)
True
print(True and False)
False
print(a == 2 and a == 1)
False
print(a == 2 or a == 1)
True
Note that a boolean can also be considered as a number:
test = True
# true is equivalent to 1
print(test, int(test), int(test)+1, int(test)-10)
True 1 2 -9
We can assign a text (so called string) to a variable name.
chunk1 = "EPITA"
chunk2 = 'Data'
print(chunk1)
print(chunk2)
EPITA Data
print("I'm a student of EPITA")
print('I"m a student of EPITA')
I'm a student of EPITA I"m a student of EPITA
Triple quotes allow to have several lines:
print("""I'm a student of EPITA, and
I'm addicted to Data""")
I'm a student of EPITA, and I'm addicted to Data
quote = """I'm a student of EPITA, and
I'm addicted to Data"""
print(quote)
I'm a student of EPITA, and I'm addicted to Data
Equivalently, we can add \n
to skip a line:
print("I'm a student of EPITA, and\nI'm addicted to Data")
I'm a student of EPITA, and I'm addicted to Data
You can also format your string in
f
before your string.print(f"I'm a student of {chunk1}, and\nI'm addicted to {chunk2}")
I'm a student of EPITA, and I'm addicted to Data
format
.print("I'm a student of {}, and\nI'm addicted to {}".format(chunk1, chunk2))
I'm a student of EPITA, and I'm addicted to Data
print("I'm a student of {1}, and\nI'm addicted to {0}".format(chunk1, chunk2))
I'm a student of Data, and I'm addicted to EPITA
# addition
print(a + 2)
print(a + 2.)
# substraction
print(b - 2)
# multiplication
print(b * 2)
3 3.0 -0.8 2.4
# division
print(3 / 2)
# integer division
print(3 // 2)
1.5 1
# power
print(3**2)
9
# modulo
print(10 % 2) # 10 is even, so modulo is 0
print(11 % 2) # 11 is odd, so modulo is 1
0 1
# add 1 to `a`
a = a + 1
print(a)
2
# a more compact way to do it:
a += 1
print(a)
# also possible for every operator seen above
3
# concatenate strings
print("concatenation " + "succeded")
print(chunk1 + " and " + chunk2)
concatenation succeded EPITA and Data
# repetions
print(chunk1 * 3)
EPITAEPITAEPITA
Let us now see how to store a collection of items (numerical and non numerical). One of the most popular container is list
.
A list is an ordered collection of items that may have different types. A list is a mutable object and therefore can be modified.
l = [2, 4, 6, "EPITA"] # a list of 4 items
print(l)
l0 = [] # an empty list
print(l0)
[2, 4, 6, 'EPITA'] []
Like string operations, we can concatenate lists, duplicate items of the list or even delete / add some items.
# outplace manner
print(l + [3, "Data"]) # concatenate
print(l * 2) # duplicate
print(l)
[2, 4, 6, 'EPITA', 3, 'Data'] [2, 4, 6, 'EPITA', 2, 4, 6, 'EPITA'] [2, 4, 6, 'EPITA']
# inplace manner
l.extend([3, "Data"]) # extend (roughly concatenate in an inplace manner)
print(l)
l.append("extension") # add one element (inplace manner)
print(l)
l.append(100) # add one element (inplace manner)
print(l)
[2, 4, 6, 'EPITA', 3, 'Data'] [2, 4, 6, 'EPITA', 3, 'Data', 'extension'] [2, 4, 6, 'EPITA', 3, 'Data', 'extension', 100]
del l[1] # delete the second item (index 1)
print(l)
[2, 6, 'EPITA', 3, 'Data', 'extension', 100]
We can also do some indexing and slicing
# indexing
print(l[0]) # print the first item (index 0) of the list l
print(l[-1]) # print the last item of the list l
print(l[4]) # print the 5th item (index 4) of the list l
2 100 Data
# slicing
print(l[1:6]) # print the items between index 1 and index 6 (excluded) of the list l
print(l[1:]) # print the items beginning at index 1 till the last items of the list l
print(l[1:6:2]) # print the items between index 1 and index 6 (excluded) of the list l spaced by the 2 steps
print(l[::-1]) # reverse the list
[6, 'EPITA', 3, 'Data', 'extension'] [6, 'EPITA', 3, 'Data', 'extension', 100] [6, 3, 'extension'] [100, 'extension', 'Data', 3, 'EPITA', 6, 2]
it is also quite easy to assign some items by specifying the indices.
print(l) # old list
l[0] = "CHANGED"
print(l) # new list
[2, 6, 'EPITA', 3, 'Data', 'extension', 100] ['CHANGED', 6, 'EPITA', 3, 'Data', 'extension', 100]
l[3:5] = ["CHANGED1", "CHANGED2"]
print(l)
['CHANGED', 6, 'EPITA', 'CHANGED1', 'CHANGED2', 'extension', 100]
some other classical methods:
# operations on numerical lists
ln = [0, 6, 7, 1, 2, 9, 10]
print(len(ln)) # lenght of the list (not only restricted to numerical items)
print(min(ln)) # the minimum of the list
print(max(ln)) # the maximum of the list
print(sum(ln)) # the sum of the list
7 0 10 35
# check if an element is in the list
print(6 in ln)
print(60 in ln)
print("EPITA" in l)
print("EPITA" not in l)
True False True False
Of course we can also have a list of lists of items
twodimlist = [[0, 1], [2, 3], ["four", 5]]
print(twodimlist)
[[0, 1], [2, 3], ['four', 5]]
A set is an unorder collection of unique items. Usual mathematical operations (union, difference) can be performed.
odd = {1, 3, 5, 5, 5, 5, 5}
even = {2, 4}
print(odd)
print(even)
{1, 3, 5} {2, 4}
print(odd - {1}) # Difference of sets
{3, 5}
print(odd | even) # Union of sets
{1, 2, 3, 4, 5}
odd.add(2) # add an item
print(odd)
{1, 2, 3, 5}
print(odd & even) # Intersection of sets
{2}
print(odd ^ even) # Complementary of the intersection of sets
{1, 3, 4, 5}
A dictionary is a table key/value which correponds to a collection of unique keys. Keys can be any immutable type (string, numbers, …).
# let us define a dictionary
d = {
'x': [[1, 0], [0, 1]],
'y': [0, 1]
}
print(d['x']) # print only the values associated to "x"
[[1, 0], [0, 1]]
d[10] = "ten" # add an item
print(d)
{'x': [[1, 0], [0, 1]], 'y': [0, 1], 10: 'ten'}
# print keys and values
print(d.keys())
print(d.values())
dict_keys(['x', 'y', 10]) dict_values([[[1, 0], [0, 1]], [0, 1], 'ten'])
"x" in d # check if a key is in the dicitonary
True
In python, a block of instructions is delimited by indentation. For example the lines included in a if
condition is written as follows:
number = 2
# all indented lines constitute a block of the `if` condition
if number > 0:
print("the proposed number is greater than 0")
number = 2*number
print("let's double the proposed number", number)
the proposed number is greater than 0 let's double the proposed number 4
just after, we can add an else
condition alongside with indented lines
number = -1
if number > 0:
print("the proposed number is greater than 0")
number = 2*number
print("let's double the proposed number", number)
else:
print("the number is negative")
the number is negative
There may be several conditions that follow one another, therefore we need the elif
condition
number = 0
if number > 0:
print("the proposed number is greater than 0")
number = 2*number
print("let's double the proposed number", number)
elif number < 0:
print("the number is negative")
else:
print("the number is equal to 0")
the number is equal to 0
There are classically two types of loops:
for
loop runs through a set, while
loop continues as long as a condition is true. As for tests, the lines included in a given loop are indented.
With the for loop we can run a set of statements, once for each item in a collection of items such as list, dictionary, set etc.
for i in [0, 1, 2, 3, 4]:
print(i**2)
0 1 4 9 16
we can use the range
function instead of manually specifying the numbers. The syntax for the range function is:
# we can use the `range` function instead of manually specifying the numbers. see the documentation.
for i in range(5):
print(i**2)
0 1 4 9 16
for i in range(2, 8, 2):
print(i)
2 4 6
Some useful commands: enumerate and zip.
for i, val in enumerate(["Python course", 'Epita', 2021]):
print(f"iteration {i}, for the value: {val}")
iteration 0, for the value: Python course iteration 1, for the value: Epita iteration 2, for the value: 2021
for x, y in zip([1, 3, 5, "odd numbers"], [2, 4, 6, "even numbers"]):
print(f"x = {x}, y = {y}")
x = 1, y = 2 x = 3, y = 4 x = 5, y = 6 x = odd numbers, y = even numbers
for loop in a concise manner:
l = [i**2 for i in range(5)]
print(l)
[0, 1, 4, 9, 16]
l = [i**2 for i in range(5) if i%2==0] # we can add conditions
print(l)
[0, 4, 16]
With the while loop we can run a set of statements as long as a condition is true.
i = 0
while i<10:
i = i + 2
print(i)
# be carefull of infinite loop. For example, if `i` is not incremented, the block of instructions will run infinitely
2 4 6 8 10
We can also create functions. A function is a block of code which only runs when it is called. You can pass parameters into a function called (input) arguments and it can return some parameters called (output) results. You can use a triple quote at the beginning of the block if you want to give a documentation for users.
def add_one(x):
"""
this function increments by 1 the variable x
"""
x = x + 1
return(x)
x_old = 2
x_new = add_one(x_old)
print(x_new)
3
We can take account several arguments in a function
def add(x, n):
"""
this function adds "n" to the variable x
"""
x = x + n
return(x)
x_old = 2
n = 5
x_new = add(x_old, n)
print(x_new)
7
We can also have default values for part (or all) input arguments.
Important remark: non-default argument follows default argument
def add(x, n = 1):
"""
this function adds "n" to the variable x
"""
x = x + n
return(x)
x_old = 2
print(add(x_old))
print(add(x_old, 1))
print(add(x_old, 4))
3 3 6
Thanks to the triple quote, you can use the function "help" on your function. a string that occurs as the first statement in a module, function, class, or method definition is called docstring.
help(add)
Help on function add in module __main__: add(x, n=1) this function adds "n" to the variable x
It is possible to check if a parameter is passed with None
:
def add(x, y=None):
"""
this function do the addition x+y. If y isn't passed then, we increments x by 1.
"""
if y is None:
x = x + 1
return(x)
else:
x = x + y
return(x)
add(1)
2
add(1, 10)
11
The present section gives a quick look at the classes and objects. We will dive more into it in other notebooks.
Our main objective is trying to define "complex types" such as
Besides, like lists or dictionaries, we want to have some "descriptions" or "states" associated to these types:
Finally, in addition to these "descriptions", we also want to "modify" their state:
Since, python is an object-oriented programming (OOP) language, it is possible to define this more "complex type" called class
Important remark: Everything in Python is an object
A class is a block of codes (like function, we have indentations) defined as follows :
class Point:
"""
Definition of the class "Point"
"""
When declaring a variable of class Point
, the python language executes the __init__
method also called constructor. It allows to define the attributes of the class directly from the parameters or as the result of a calculation or a function. The constructor, like all the other methods, has as its first parameter self which allows access to the attributes and methods of the class.
Attributes are most often declared inside the constructor. More generally attributes can be defined in two ways:
attr
for example) is defined and assigned as follows self.attr = ...
obj
, the attribute attr
is defined and assigned as follows obj.attr = ...
class Point:
"""
Definition of the class "Point"
"""
def __init__(self, x, y): # this function/method is used for instantiation
self.x = x # assign the attribute x. "self" is used for pointing the declared object
self.y = y # assign the attribute y. "self" is used for pointing the declared object
self.norm = (x**2 + y**2)**(0.5) # assign the attribute norm corresponding to the norm of the coord (x,y)
A method is a function defined inside a class. It invariably accepts at least one parameter which is self
as in the previous example. The following program is equivalent to the first one.
class Point:
"""
Definition of the class "Point"
"""
def __init__(self, x, y): # this function/method is used for instantiation
self.x = x # assign the attribute x. "self" is used for pointing the declared object
self.y = y # assign the attribute y. "self" is used for pointing the declared object
self.norm = self.compute_norm() # assign the attribute norm corresponding to the norm of the coord (x,y)
def compute_norm(self): # define a method called "compute_norm"
norm = (self.x**2 + self.y**2)**(0.5)
return norm
Let us create a new instance of the class and assigns this object to the local variable p
.
p = Point(2, 3)
Print the coordinate of the point p:
print(f"coordinate of the point: ({p.x}, {p.y})")
print(f"norm of the point: {p.norm}")
coordinate of the point: (2, 3) norm of the point: 3.605551275463989
One can also easily use the method of the object:
print(p.compute_norm())
3.605551275463989
Like any variables, we want to have some operations associated to these (same class) objects. As
a = 1
b = 3
print(a + b)
gives you 4, one would want operations for points: for example we except that a point $(1, -1)$ added by a point $(1, 1)$ gives a new point $(2, 0)$. It is possible if we define the operator __add__
.
Operators are methods that allow a simpler manipulation of objects. By convention their name start and end with __
.
class Point:
"""
Definition of the class "Point"
"""
def __init__(self, x, y): # this function/method is used for instantiation
self.x = x # assign the attribute x. "self" is used for pointing the declared object
self.y = y # assign the attribute y. "self" is used for pointing the declared object
self.norm = self.compute_norm() # assign the attribute norm corresponding to the norm of the coord (x,y)
def compute_norm(self): # define a method called "compute_norm"
norm = (self.x**2 + self.y**2)**(0.5)
return norm
def __add__(self, another_point) :
return Point(self.x + another_point.x, self.y + another_point.y)
p1 = Point(1, -1)
p2 = Point(1, 1)
p3 = p1 + p2
print(f"p1 = ({p1.x}, {p1.y})")
print(f"p2 = ({p2.x}, {p2.y})")
print(f"p3 = ({p3.x}, {p3.y})")
p1 = (1, -1) p2 = (1, 1) p3 = (2, 0)
Up to now, we only used the internal libraries of python. Using external libraries is possible and is quite easy to import in python.
Let us compute the square root of a number. For that purpose, we can import the library (aka package) numpy
(module used for numerical analysis). There are several ways to do it and we show bellow some recommended methods.
import numpy
numpy.sqrt(2)
1.4142135623730951
import numpy as np
np.sqrt(2)
1.4142135623730951
from numpy import sqrt
sqrt(2)
1.4142135623730951
To create a module, store the functions and variables definitions in a Python file. For example, we can create a my_module.py file containing:
Let us import this module and use the function add
:
import my_module as my
my.add(5, 7)
12
my_module.py
can also be executed as a script. In this case
will be taken into account and call_me()
will be called:
run my_module.py
hello world!
When an object is initiated, it is assigned an unique object ID (we recall that every variables are object instances). Its type cannot be modified but its state can be changed if it is mutable. In summary, after it instantiation, a mutable object can be changed whereas an immutable object cannot be changed. In a nutshell, we listed a (non-exhaustive) immutable and mutable types:
thanks to the built-in functions id
and type
(see documentation), we can easily check the id and the type of an object.
Let us create a string variable and copy it to another variable name.
u = "EPITA"
v = u
print(type(u))
print(type(v))
print(id(u))
print(id(v))
print(id(u) == id(v))
<class 'str'> <class 'str'> 140324596336624 140324596336624 True
if we change v
does u
change in consequence ? -> No, immutable objects doesn’t allow modification after creation
v += "AAA"
print(id(u) == id(v))
print(u)
print(v)
False EPITA EPITAAAA
Let us create a list variable and assign it to another variable name.
l = ["EPITA", "Data"]
t = l
print(id(t) == id(l))
print(t)
print(l)
True ['EPITA', 'Data'] ['EPITA', 'Data']
if we change t
does l
change in consequence ? -> Yes, mutable objects allow modification after creation, therefore if a list l
is assigned with the operator =
to t
then it merely created a second name to name the same list.
t.append("Science")
print(id(t) == id(l))
print(t)
print(l)
True ['EPITA', 'Data', 'Science'] ['EPITA', 'Data', 'Science']
If we want to copy a list, we need to use the library copy
.
import copy
l = ["EPITA", "Data"]
t = copy.copy(l) # use function deepcopy if the list contains some mutable objects
t.append("Science")
print(id(t) == id(l))
print(t)
print(l)
False ['EPITA', 'Data', 'Science'] ['EPITA', 'Data']
google it to find at least some clues, recommended: search in stackoverflow
cheatsheets for python and for specific and well-known packages OR stackoverflow/search engine
you can, or you can also use tools for sharing whole repertory (usualy in the cloud like DropBox or GoogleDrive)
think of printing/logging within your code in order to find out where we have to debug
write more small functions instead of big functions
Create the following list with loops:
[['car', 0, 1, 4, 9, 16],
['bus', 1, 4, 9, 16, 25],
['train', 4, 9, 16, 25, 36],
['boat', 9, 16, 25, 36, 49]]
Create a script that prints this list in the following manner:
car 0 1 4 9 16
bus 1 4 9 16 25
train 4 9 16 25 36
boat 9 16 25 36 49
For the following list of dictionaries, write a script that add a field registrations which is twice the number of accepted papers.
confs = [{"Name": "NeurIPS", "Date": 2016, "Location": "Barcelona", "acc_papers": 300},
{"Name": "ICML", "Date": 2016, "Location": "New York City", "acc_papers": 450},
{"Name": "ICML", "Date": 2015, "Location": "Lille", "acc_papers": 250},
{"Name": "AISTATS", "Date": 2016, "Location": "Cadiz", "acc_papers": 100}]
Write a function include, that produces the following results:
>>> l = [0]
>>> include(l, 2)
>>> print(l)
[0, 2]
>>> include(l, "-1")
>>> print(l)
[0, 2, '-1']
>>> include(l)
>>> print(l)
[]
Create a module, that contains a function fibonacci(n) computing the $n^{th}$ Fibonacci number. Then create a script using this function.
Change the class Point
(the toy example bellow) in order to take account a 3rd coordinate z
.
class Point:
"""
Definition of the class "Point"
"""
def __init__(self, x, y):
self.x = x
self.y = y
self.norm = self.compute_norm()
def compute_norm(self):
norm = (self.x**2 + self.y**2)**(0.5)
return norm
def __add__(self, another_point) :
return Point(self.x + another_point.x, self.y + another_point.y)
Then execute the following script.
p1 = Point(1, -1, 1)
p2 = Point(1, 1, 3)
p3 = p1 + p2
print(f"p1 = ({p1.x}, {p1.y}, {p1.z})")
print(f"p2 = ({p2.x}, {p2.y}, {p2.z})")
print(f"p3 = ({p3.x}, {p3.y}, {p3.z})")