Lecture 1 : Basics of Python¶
November, 2021 - François HU
Master of Science in Artificial Intelligence Systems - EPITA
This lecture is available here: https://curiousml.github.io/
Introduction ¶
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, functions and exceptions.
Note that we can easily check documentations about an object or function with the function help()
or ?
.
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.
1. Variables ¶
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:
- ordered collections such as list or range,
- unordered collections such as set or dict
Note that these variables are built into the interpreter.
1.1. Numbers: integer, float and complex ¶
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
1.2. Booleans ¶
We can create a boolean by:
- creating a true/false conditional statement or
- using directly
True
andFalse
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
1.3. Strings ¶
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
- adding
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
- with the method
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
- in additional, you can add positions.
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
1.4. Operations on variables ¶
on numbers¶
# 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
on strings¶
# concatenate strings
print("concatenation " + "succeded")
print(chunk1 + " and " + chunk2)
concatenation succeded EPITA and Data
# repetions
print(chunk1 * 3)
EPITAEPITAEPITA
1.5. Collection of items: List, Set and Dictionary ¶
Let us now see how to store a collection of items (numerical and non numerical). One of the most popular container is list
.
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 indexes.
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]]
Sets¶
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}
Dictionaries¶
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
2. Tests and loops ¶
2.1. Conditional statements ¶
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
2.2. Loops ¶
There are classically two types of loops:
- the
for
loop runs through a set, - the
while
loop continues as long as a condition is true.
As for tests, the lines included in a given loop are indented.
The for loop¶
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:
- range(stop) or
- rang(start, stop, step) with third parameter optional (see the documentation for more information).
# 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]
The while loop¶
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
3. Functions ¶
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
4. Introduction to classes and objects ¶
The present section gives a quick look at the classes and objects. We will dive more into it in other notebooks.
4.1. Definitions ¶
Our main objective is trying to define "complex types" such as
- a point in 2D (or 3D) space
- a person, a car
- a graph, ...
Besides, like lists or dictionaries, we want to have some "descriptions" or "states" associated to these types:
- for a point: the coordinate $(x, y)$
- for a person: his age, height, name, salary, ...
- for a node: his name, value, neighbor nodes, ...
Finally, in addition to these "descriptions", we also want to "modify" their state:
- for a point: compute the L2 norm or add two points
- for a person: increase his salary
- for a node: calculate the number of neighbouring nodes
Since, python is an object-oriented programming (OOP) language, it is possible to define this more "complex type" called class
- Assigning a class to a variable name is called instantiation (or instantiating a class) and the resulted variable is called an object.
- The associated "descriptions" are called attributes
- and one can define methods in order to "modify" the state.
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"
"""
4.1. Attributes and constructor ¶
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:
- inside the class: in any methods, an attribute (
attr
for example) is defined and assigned as followsself.attr = ...
- outside the class: for an object named for example
obj
, the attributeattr
is defined and assigned as followsobj.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)
4.2. Methods ¶
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
4.3. Instantiating a class ¶
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
4.4. Operator ¶
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)
5. Modules ¶
Up to now, we only used the internal libraries of python. Using external libraries is possible and is quite easy to import in python.
5.1. Import an existing module¶
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.
method 1: load directly the library¶
import numpy
numpy.sqrt(2)
1.4142135623730951
method 2: load the library with a different name (usually a shorter name)¶
import numpy as np
np.sqrt(2)
1.4142135623730951
method 3: load only the wanted function from the library¶
from numpy import sqrt
sqrt(2)
1.4142135623730951
5.2. Create a module¶
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!
6. Immutable and mutable types ¶
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:
- immutable built-in types: int, float, complex, bool, string;
- mutable built-in types: list, set, dict;
- others: custom classes are mutable (unless one override attribute setting).
6.1. Immutable 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
6.2. Mutable types¶
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']
6.3. Avoid modification for mutable types¶
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']
7. Some practical tips ¶
What if I struggle too much or if I want to fix an error ?¶
google it to find at least some clues, recommended: search in stackoverflow
What if I cannot remember a specifi python syntax ?¶
cheatsheets for python and for specific and well-known packages OR stackoverflow/search engine
What if I want to share my code to someone ? Do I send an email ?¶
you can, or you can also use tools for sharing whole repertory (usualy in the cloud like DropBox or GoogleDrive)
How to debug my code ?¶
think of printing/logging within your code in order to find out where we have to debug
How to have a good program ?¶
write more small functions instead of big functions
Exercices ¶
Exercice 1.¶
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
Exercice 2.¶
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}]
Exercice 3.¶
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)
[]
Exercice 4.¶
Create a module, that contains a function fibonacci(n) computing the $n^{th}$ Fibonacci number. Then create a script using this function.
Exercice 5.¶
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})")