Basics

Download as pdf or txt
Download as pdf or txt
You are on page 1of 124

• Materiale didattico

– slides
– esercizi risolti

• Esame: Esercizi di programmazione da svolgere al computer


– 1 ora
– serie di esercizi di programmazione in python / matlab

• DATE esami
– 20/6 ore 9
– 5/7 ore 9
– 30/7 ore 14
• [ITA] Introduzione a python, Tony Gaddis
• Think Python, by Allen B. Downey

• The Coder’s Apprentice Learning Programming with Python 3, by Pieter


Spronck [Free PDF]

• A Whirlwind Tour of Python, by Jake VanderPlas


• Python Data Science Handbook, by Jake VanderPlas
• Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow, by
Aurélien Géron
Python Basics
• variables
• elementary data types
• expressions
• data structures
• conditional statements
• cycles
• functions
• files
• exceptions
• classes
python
• First public release in 1991 by Guido Van Rossum (the
Benevolent Dictator for Life)
• Python2 released in 2000, and python3 in 2008
• Interpreted
• Dynamically typed (you don’t need to declare
variables)
• Object oriented
• Low number of native symbols

• Widely used for data analyses, scientific computing,


and Artificial Intelligence (many libraries)

• Not particularly efficient (compared to C++/Fortran)


• But good performance for data analysis can be
achieved using libraries
https://www.tiobe.com/tiobe-index/

Why python ?
Where to get python from
Freely available (for any platform) at http://www.python.org/

Suggestion: Download python within the anaconda platform


https://www.anaconda.com/distribution/
– package manager (conda)
– text editor (spider)
– iterative interpret (ipython)
How to execute python code
• Using a GUI (like spider)
How to execute python code
• Using jupyter notebook / jupyter lab
Comments
• Everything after the ‘#’ character is treated as a
comment, and it is not executed
• Use comments to document what you’re doing

# this is not executed


print(“SpamSpamSpam”) # … this is also not executed
One instruction = One line
• But long instructions can be broken over multiple lines by:
– using the \ character
– just going to the next line if the text is included in between parenthesis
• But it is possible to write more instructions on a line, if separated by ;

x=3*2\ x = 3 * (2 +
(5 + 4) 6)
x = 0; y = 0
BLANK SPACES
• Indentation is used to define blocks of code
• The number of blank spaces at the beginning of the line is arbitrary
(multiple of 4 is the standard) but it needs to be consistent within the
block
• Spaces within a line are meaningless C++
if (x > y) {
cout << ‘x is greater than y’ << endl;
if (x > y): dist = x – y;
print(‘x is greater than y’) } else {
This is a block of code
dist = x – y dist = y – x;
else: }
dist = y – x cout << dist << endl;
print(dist)
print(‘SpamSpamSpam’)
print ( ‘SpamSpamSpam’ )
These two lines are identical
VARIABILE_NAME = EXPRESSION
Binding: a logical name is associated with an object (the result of
the expression)

movie = “Life of Brain” x=1


print(movie) # it’s the same as print(“Life of Brain”) print(x)

• The logical name is similar to a pointer to an object


– Different names can point to the same object
– The type is associated with the object not with the name (there’s an
overhead compared to statically typed languages as C)
Python names

• Sequences of arbitrary length of alphanumeric characters and


underscores
– The first character cannot be a number
– Names that start with underscore have special meanings
– Do not use spaces inside names
– Names are case sensitive
Elementary data types (they are objects)
Data type Values Examples
bool True, False
int Integer numbers (no limits in length) 1234
It is possible to use bases different from 10 with 0b10
the prefixes: 0b, 0B (binary); 0o, 0O (octal); 0x, 0X 0x1a
(hexadecimal)
float floating numbers 0.01
lowest absolute value: 5e-324 .01
highest absolute value: 1e308 1e-2
1E-2
complex complex numbers 2.1+3.4j
complex(2.3, 3.4)
str string of characters “A”
Both single or double apexes can be used ‘ATCG’
Use three apexes for strings spanning more than “””ATC
one line CFT”””
NoneType None

Basic data types are immutable


Data type
• The type of the variable is not explicitly defined
• It is associated with the object itself and not with its name
• There’s an overhead compared to programming languages like C
– E.g.: an int is actually an object, that stores the actual integer number plus other information (the data type)

C
python
int a;
a = 1 # int
float b;
b = 1.0 # float
bool c;
c = True # bool
a = 1;
b = 1.0;
c = True;

• Variables still have a type, it’s just not explicitly defined by the programmer
Beware of rounding
Floating points are stored as binary numbers à rounding are possible
It is usually a bad idea to test if two floating numbers are the same
Functions
• Block of codes that perform a specific operation
• They can receive inputs
• They can return an output
• Some input parameters might have default values (keyword parameters)

• When using a function you don’t need to know anything about how it works

INPUT[s] FUNCTION OUTPUT[s]


return_value = function_name(parg1, parg2, ….,
karg1 = val1, karg2 = val2, ….)

• everything in blue is not compulsory

• positional arguments:
• the position is important
• some positional argument might be compulsory
• an arbitrary number of positional arguments might follow

• keyword arguments:
• the position is not important
• if a keyword argument is not defined, the default value is used
• there can be an arbitrary number of keyword arguments
type(True) à bool
type(object): return the type(1) à int
type(1.0) à float
type of an object

isinstance(object, type): return isinstance(False, bool) à True


isinstance(1.0, int) à False
True if the object is of that type
print()
• It shows in the standard output the input argument(s)
• It can take an arbitrary number of arguments
– print(‘John’)à John
– print(‘John’, ‘Cleese’) à John Cleese
• At default, the input parameters are printed separated by a space, but it is possible to change this
behaviour with the argument sep
– print(‘John’, ‘Cleese’, sep = ‘-’) à John-Cleese
• At default, the last printed character is the newline, but it is possible to change it with the argument end
• With flush = True, immediate writing to stdout is forced

print(‘Cleese’) Cleese
print(‘Gilliam’) Gilliam

print(‘Cleese’, end = ‘,’)


Cleese,Gilliam
print(‘Gilliam’)
input()
• It shows in the standard output the input argument (if any)
• Then it waits for input from the standard input up to a newline
character
• It returns the input from standard input as a str

it means that the cell is being executed. In this case the execution
ends when typing the new line character
help()
• For any object it returns the help string
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.

x=1
Try:
help(x)
help(object) Return the help string for the object

abs(number) Return the absolute value abs(-3.2) à 3.2


pow(base, exp) Return base^exp pow(2,3) à 8
round(number) Round the number to the closest integer round(1.9) à 2
bin(int), hex(int), oct(int) Convert an integer number into a string containing its bin(4) à'0b100’
binary/hexadecimal/octal representation
bool(object), int(object), Convert the object into a bool/int/float/str (when
float(object), str(object) possible)

complex(real [, imag]) Return a complex number with real and imag part comples(1,2) à 1+2j

chr(int) Return the unicode character with that index chr(8364) à €


chr(0x1F92F) à🤯
ord(chr) Return the unicode index of the character ord(‘€’) à 8364

isinstance(object, type) Return True is the object is of class type isinstance(1,int) à True
type(object) Return the type of the object type(1) à int
bool(), int(), float(), complex(), str()
• Convert input to an object of the corresponding type

bool(1) à True
bool(0) à False
bool(‘any not empty string’) à True
bool(‘’) à False

complex(1) à 1+0j
complex(1,2) à 1+2j
int(‘12’) à 12 complex(‘1+2j’) à 1+2j
int(‘12.1’) à Error

These are not functions, they are classes


Python Expressions
• set of commands that when evaluated by the interpret return an
object
• Examples: arithmetic expressions

(3 * 2) + 4 Since expressions are evaluated by the


interpret, they can be used on the right
4 – 7*5 side of binding operations
x=1+4
x=1+y
x=1+x
Arithmetic operators
Operator Examples
+ Sum 1+7 = 8
True + 1 + False = 2
# conversion rules are automatically applied
- Subtraction
* Product

/ Division When mixing int with float, everything is converted


to float (in python3, it’s the opposite in python2)
// Quotient 5 // 2 = 2
1 // 0.3 = 3
% Module 5%2=1
1 % 0.3 = 0.1
** Power 3**2 = 9
Operators can be bool, int, float, complex
In numeric expressions, True is treated as 1, False as 0
The results always belongs to the wider set (bool < int < float < complex)
Combining binding with operators
All the arithmetic operators can be combined with the binding
operator into a single expression

x = x + 1 à x += 1
x = x – 2 à x -= 2
x = x * 3 à x *= 3
x = x / 4 à x /= 4
x = x // 5 à x //= 5
x = x % 7 à x %= 7
x = x ** 4 à x **= 4

Elementary types are immutable, so x += 1 defines a new object


+/* Operators with strings
“A” + “C” “AC”

“ATCG”*4 “ATCGATCGATCGATCG”
4*“ATCG”

Both can be combined with the binding operator

s = ‘Spam’ s = ‘Spam’
s += ‘Spam’ à s = ‘SpamSpam’ s *= 3 à s = ‘SpamSpamSpam’
in Operator
object1 in object2
True if object1 is included in object2
It can be used any time object2 is iterable
“TATA” in “ACGTACGCTATACG” True

“TATA” in “AT” False

1 in 12 ERROR

‘1’ in ‘12’ True


not in Operator
Logical not of the in operator

“TATA” in “ACGTACGCTATACG” False

“TATA” in “AT” True


Logical operator
NOT

True False

False True

AND True False OR True False

True True False True True True

False False False False True False


Rules for Boolean conversions

• int/float/complex are True if not zero

• strings are True if not empty

• None is considered False


Python logical operator
Operator Example Result
not Return a boolean value not False True
not 0 True
not 1 False
not “ATCG” False
not ”” True
and Return: True and False False
• the second operand when True 1 and 0 0
• the False operand when only one is 1 and 3 3
False 0 and “” 0
• the first operand when both are False
or Return: True or False True
• the first operand when both are True 1 or 0 1
• the True operand when only one is True 0 or 1 1
• the second operand when both are
False
Relational operators
Operator Example Result

== Equal 1 == 0 False
“ATC” == “ATC” True

!= Different “ATGC” != “ATC” True


“A” != 1 True

< “AC” < “AT” True


“AT” < “AT” False

>

<= “AT” <= “AT” True

>=
Bitwise operators
Operator Example Result

& and 2&6 2


2 = 0b10
6 = 0b110
| or 2|6 6

^ xor 2^6 4

~ not

<< left shift 2<<1 4

>> right shift 2>>1 1

They can be combined with the binding operator


Priority when evaluating expressions
Operator
()
function call
Slicing
accessing attributes
** When in doubt, use
*, /,//, %
parentheses !
+, -
relational operators
in, not in
not
and
or
IF (1/3)


if expression:
<Block of code>

• Indentation is used to define the block of code that is executed only when the
expression is True
• Be consistent in using tabs/spaces for indentation throughout the code
IF (2/3)

if expression1:
<Block of code 1>
else:
<Block of code 2>
IF (3/3)
if expression_1:
<Block of code 2>
Only the block of code corresponding
elif expression_2: to the first True expression is executed

<Block of code 2>


[…]
elif expression_N:
<Block of code N>
else: The final else might be missing

<Block of code N + 1>


if on a single line

expression if condition else expression

x if x > 0 else -x

x-y if x > y else y-x


UNICODE
• Python uses the UNICODE
standard for characters
• The default encoding is UTF-
8 (ASCII is a subset of
UNICODE)
• Variable number of bytes is
used for coding (depending
on the character)
Special characters
\n = Newline
\t = tab
\xNN = hexadecimal ASCII code
\uNNNN = UTF-8 code
\N{name} = UTF-8 name
str Indexing s = ‘abcdefgh’

a b c d e f g h
0 1 2 3 4 5 6 7
-8 -7 -6 -5 -4 -3 -2 -1

len(s) Return the number of elements in s

s[i] i-th element


s[i:j] from the i-th element to the one before the j-th element
s[i:] from the i-th element to the last one
s[:j] from the first element to the one before the j-th element
s[:] all the elements in the str
s[i:j:k] from the i-th element to the one before the j-th element,
selecting elements with step k
s[-i] i-th element reading from the end, the index of the last
element is -1
LOOPS


while expression:
<Block of code>

for variabile in iterable:


<Block of code>
break: stop the execution of the loop

continue: move straight to the next execution of the loop

else: the following block of code is executed only if the


loop terminates without a break
while True:
x = input(‘Provide a positive number: ‘)
x = int(x)
if x > 0:
print(‘x = ‘,x)
break

i=0
while i < 10:
import math
x = input(‘Provide a positive number: ‘)
l = [1.1, 3.2, -0.1]
for x in l: x = int(x)
if x < 0: if x > 0:
continue print(‘x = ‘,x)
print(‘log(x) = ‘,math.log(x)) break
i += 1
else:
print(‘I surrender !’)
Methods
• Similar to functions but they operate on a specific object
• They can change the object itself
• Positional and keyword parameters work as in function

INPUT[s] FUNCTION OUTPUT[s]

OBJECT.

INPUT[s] METHOD OUTPUT[s]


x = 1 + 2j
x.conjugate() à 1-2j

s = ‘a usEleSs sTRIng’
s.lower() à ‘a useless string’
S = s.upper() à ‘A USELESS STRING’
S.replace(’STRING’, ‘EXAMPLE’) à A USELESS EXAMPLE
s.lower() Return a string with all the characters converted to lower case
s1 = ‘Wanda’
s2 = s1.lower() à s2 = ‘wanda’
s.upper() As before, but converting to upper case
s.find(substring) Search for substring into s. It returns the position of the first occurence (indexes
start at zero). If substring is not included in s, it return -1.
s1 = ‘SpamSpamSpam’
i = s1.find(‘am’) à i = 2
s.strip(remove_chr) Remove all the characters in remove_chr at the begin/end of s. At default it
removes blank spaces and new lines.
s1 = ‘ ATCG ‘
s2 = s1.strip() à s2 = ‘ATCG’
s.replace(old,new) Return a new string where any occurance of old is replaced by new
dna = ‘ATCGTCG’
rna = dna.replace(‘T’,’U’)
s.join(list) Create a string by merging the elements in seq using s as a separator

s.split(sep) Split a string into a list using sep as separator


s = ‘deaD paRrot’
s.capitalize(), s.title() Dead parrot, Dead Parrot
s.center(20) ' deaD paRrot ‘
s.ljust(20) 'deaD paRrot ‘
s.rjust() ' deaD paRrot'
s.count(‘ea’) 2
s.endswith(‘ot’), s.startswith(‘de’) True, True
s.find(‘a’), s.rfind(‘a’) 2, 6 (-1 if not found)
s.index(‘a’), s.rindex(‘a’) 2, 6 (Error if not found)
s.replace(‘ea’,’e’) deD paRrot
s.isalpha() True
s.isdecimal(), s.isdigit(), s.isalnum() False
s.isnumeric(), s.isspace(), s.istitle()
s.isupper(), s.islower() False
s.upper(), s.lower(), s.swapcase() DEAD PARROT, dead parrot, DEAd PArROT
s.strip(‘D’), s.rstrip(), s.lstrip() eaD paRrot

Strings are immutable à the methods cannot change the string, they return a new one
format strings
It creates formatted strings by substituting the parts between
curly brackets by the actual values provided to the method
‘This {} weights {} kg’.format(’dog’, 16)
à This dog weights 16 kg

it is possible to enumerate the items

‘This {0} weights {1} kg’.format(’dog’, 16) ‘This {1} weights {0} kg’.format(16, ’dog’)
à This dog weights 16 kg à This dog weights 16 kg

… or to give names to them

‘This {animal} weights {w_kg} kg’.format(animal = ’dog’, w_kg = 16)


à This dog weights 16 kg
… and to define the format style
‘This {animal:s} weights {w_kg:f} kg’.format(animal = ’dog’, w_kg = 16)
à This dog weights 16.0000 kg

‘This {animal:s} weights {w_kg:4.2f} kg’.format(animal = ’dog’, w_kg = 16)


à This dog weitghs 16.00 kg

s string
< left-justified {:justificationfield_witdhfield_type}
c character
^ centered
d integer, base 10
> right-justified s = ‘{:3.1f}'.format(1.0)
f floating print(s)
e exponential s = '{:<10s}'.format('dog') s = ‘{:4.2f}'.format(1.0)
notation print(s) print(s)
dog
b binary s = ‘{:^10s}'.format('dog') dog
print(s) dog 1.0
o octal 1.00
s = ‘{:>10s}'.format('dog')
x hexadecimal print(s)
Built-in data structures

list Ordered collection mutable


tuple Ordered collection immutable
dict Unordered collection with mutable
keyword access
set Unordered collection mutable
frozenset Unordered collection immutable
bytes Ordered collection of bytes immutable
bytearray Ordered collection of bytes mutable
LIST

• Ordered collection of elements à each element has an index

• The elements can be of any kind

• Elements of different kinds can be mixed in the same list

• Lists can be modified


Examples of list definition
L = [‘A’, ‘C’, ‘T’, ‘G’] [‘A’, ‘C’, ‘T’, ‘G’]

L = [‘AA’, ‘AT’, ‘AC’, ‘AG’] [‘AA’, ‘AT’, ‘AC’, ‘AG’]

L = [‘A’, ‘C’, ‘T’, ‘G’, ’A’, ’T’] [‘A’, ‘C’, ‘T’, ‘G’, ’A’, ’T’]

L = list(‘TCAATGCG’) [‘T’, ’C’, ‘A’, ‘A’, ‘T’, ‘G’, ‘C’, ‘G’]


This syntax can be used with any
iterable object
L1 = [‘A’, ‘T’]
L2 = [‘C’, ‘G’]
L3 = [L1, L2] [ [‘A’, ‘T’], [‘C’, ‘G’] ]
Indexing
3.2 ‘A’ ‘ATCG’ 7.8 True 3.4 3.2 3.2
0 1 2 3 4 5 6 7
-8 -7 -6 -5 -4 -3 -2 -1

len(L) Return the number of elements in L

L[i] i-th element


L[i:j] from the i-th element to the one before the j-th element
L[i:] from the i-th element to the last one
L[:j] from the first element to the one before the j-th element
L[:] all the elements in the list
L[i:j:k] from the i-th element to the one before the j-th element,
selecting elements with step k
L[-i] i-th element reading from the end, the index of the last
element is -1
Using indexing to modify a list
L[i] = x Change the i-th element to the value x
L[i:i] = M Add the elements of M before the i-th element of L (M
needs to be an iterable)
L = [1,2,3]
L[1:1] = [4,5] à [1,4,5,2,3]
L[i:j] = M Change the elements of L from i to j-1 with the elements
of M (M is an iterable)
L = [1,2,3]
L[1:2] = [4,5] à [1,4,5,3]
L[len(L):len(L)] = M Add the elements of M at the end of L

del L[i] Remove i-th element from the list. It changes the list
itself
del L[i:j] Remove elements for i to j-1
del L[i:j:k] Remove elements from i to j-1 with step k
l = [32, 17, 1, 8, 21, 9]

l[1] = 18 à [32, 18, 1, 8, 21, 9]

l[1:1] = [89,] à [32, 89, 18, 1, 8, 21, 9]

l[1:3] = [17,] à [32, 17, 1, 8, 21, 9]

del l[3:5] à [32, 17, 1, 9]


Methods of lists
L.append(x) Add the element x at the end of L
L.extend(M) Add all the elements in M (iterable) at the end of L
L.insert(i,x) Add the element x to L at position i
L.remove(x) Remove the first occurrence of x from x
Error if L does not include x
L.pop([i]) Remove and return the i-th element of the list (last
element by default)
L.index(x) Return the index of the first occurrence of x in L
Error if L does not include x
L.count(x) Count the occurrences of x in L
L.reverse() Reverse the order of the elements in L
L.sort([reverse = False]) Sort the elements in the list in ascending order
(descending if reverse is True)

Lists are mutable à these methods can modify the list itself
l = [32, 17, 1, 8, 21, 9]

l.append(8) à [32, 17, 1, 8, 21, 9, 8]

l.extend([17,35]) à [32, 17, 1, 8, 21, 9, 8,17,35]

L.remove(8) à [32, 17, 1, 21, 9, 8,17,35]

L.sort() à [1, 8, 9, 17, 17, 21, 32, 35]


Operators +,*,in, not in

l1 = [3,2,4]
l2 = 2*l1 à 3,2,4,3,2,4
l3 = [7,8] + l2 à 7, 8, 3, 2, 4, 3, 2, 4
8 in l3 à True
Common functions for lists
L = [0,1,2,3]
len(L) 4 Number of elements
min(L) 0
max(L) 3
sum(L) 6
any(L) True True if any element is True
all(L) False True is all the elements are True

They work for any iterable


If the corresponding operator is defined
(e.g. + for sum, > for max, etc)
• range(N): return an iterable to the first N-1
integer values
– range(N, M): return an iterable to the interger
values from N to M-1
Aliasing
animals_1 = [‘dog’, ‘cat’ , ‘mouse’] animals_1 = [‘dog', ‘fish', ‘mouse']
animals_2 = animals_1 animals_2 = [‘dog', ‘fish', ‘mouse']
animals_2[1] = ‘fish’

• The orange command does not create a new list

• It creates a new label for the same object pointed by animals_1

• Thus, any change to animals_2 also affects animals_1


is operator

It checks if two names point to the same object


(not if they are equal)

l1 = [1,2,3] l1 = [1,2,3] l1 = [1,2,3]


l2 = l1 l2 = [1,2,3] l2 = [1,2,3]
l1 is l2 à True l1 is l2 à False l1 == l2 à True
copy method
It creates a copy of the list
l1 = [1,2,3]
l2 = l1.copy()
l1 is l2 à False

l1 = [1,2,3] But it does not copy the elements of the list !

l2 = [4,5,6]
l3 = [l1, l2] from copy import deepcopy
l4 = l3.copy()
l4 is l3 à False l1 = [1,2,3]
l2 = [4,5,6]
l4[0] is l3[0] à True l3 = [l1, l2]
l4 = deepcopy(l3)
l4 is l3 à False
l4[0] is l3[0] à False
Aliasing and binding operator

l1 = [1,2,3]
The l1 defined in the last line points to a new
l2 = l1 object, so l2 still points to [1,2,3]
l1 = l1 + [4,5,6]

l1 = [1,2,3] Here, the l1 defined in the last line points to the


l2 = l1 same object defined in the first line, so in this
case also l2 points to [1,2,3,4,5,6]
l1 += [4,5,6]
list Vs strings
strings are similar to lists, but with two important differences:
• all the items are characters
• strings are immutable

s = ‘dog’ l = list(‘dog’)
s[0] = ‘f’ à ERROR l[0] = ‘f’ à now it works
tuples
• Similar to list but immutable

t1 = (1,2,3) with or without parenthesis is the same


t2 = 4,5,6
t1[0] = 2 à ERROR t1 = () # empty tuple
t2 = 2, # one element tuple

l1 = [1,2,3]
l1 = [1,2,3] t1 = tuple(l1) # it works with any iterable
l2 = [4,5,6]
t1 = l1, l2
elements can be mutable objects
SET

• Unordered collection of elements

• Elements can be of different types

• Being unordered, indexing is not possible (and each object is


included only once)

• Elements need to be hashable objects (immutable objects are


hashable)
S = {‘A’, ‘C’, ‘T’, ‘G’} {‘A’, ‘C’, ‘T’, ‘G’}

S = {‘AA’, ‘AT’, ‘AC’, ‘AG’} {‘AA’, ‘AT’, ‘AC’, ‘AG’}

S = {‘A’, ‘C’, ‘T’, ‘G’, ’A’, ’T’} {‘A’, ‘C’, ‘T’, ‘G’}

S = set(‘TCAATGCG’) {‘A’, ‘C’, ‘T’, ‘G’}


It works with any iterable object

S = { {‘A’, ’T’}, {‘C’, ’G’} } Error: sets are not hashable


S = { [‘A’, ’T’], [‘C’, ’G’] } Error: lists are not hashable
S = { (‘A’, ’T’), (‘C’, ’G’) } OK: tuples are hashable

S = {‘A’, 1, True, 2.7} {‘A’, 1, True, 2.7}


Set operations

Operator Method

SET1 | SET2 SET1.union(SET2,…) SET1 = {1,2,3}


SET2 = {1,4,5)
SET1 | SET2 à {1,2,3,4,6}
SET1 & SET2 SET1.intersection(SET2,…) SET1 & SET2 à {1}
SET1 – SET2 SET1.difference(SET2,…) SET1 – SET2 à {2,3}
SET1 ^ SET2 SET1.symmetric(SET2,…) SET1 ^ SET2 à {2,3,4,5}

They do not modify SET1, they return new objects


Adding/removing elements

OPERATORE METODO EFFETTO


SET1.add(item) SET1 = {1,2,3}
SET1.add(4) à SET1 = {1,2,3,4}
SET1.remove(item) SET1.remove(4) à SET1 = {1,2,3}
Error if item is missing
SET1.discard(item) As before, but without error if item is
missing
SET1 |= SET2 SET1.update(IT) Add all the elements in IT to SET1, where
IT is an iterable
SET1.update([4,5,6])
à SET1 = {1,2,3,4,5,6}

They directly modify SET1


Set comparisons
Operator Method
SET1.isdisjoint(SET2) True if there’s no element in common
SET1 <= SET2 True if all the elements in SET1 are also in
SET2
SET1 < SET2 SET1.issubset(SET2) True if SET3 is bigger than SET1, and all
the elements in SET1 are also in SET2
SET1 >= SET2
SET1 > SET2 SET1.issuperset(SET2)
FROZENSET
• As sets but hashable/immutable

s1 = {1,2,3} it works with any iterable


s1.add(4)
s2 = frozenset(s1)
s2.add(5) à ERROR
s3 = { frozenset({1,2}), frozenset({3,4}) }

it would not work with normal sets


DICTIONARY
• set of key:value pairs
• key needs to be hashable
• values can be anything

D = {‘A’: ‘adenine’, ‘T’: ‘thymine’, key value


‘A’ ‘adenine’
‘C’:’cytosine’, ‘G’: ‘guanine’}
‘T’ ‘thymine’
‘C’ ‘cytosine’
‘G’ ‘guanine’
D = {} Empty dictionary
D[k] Get the value corresponding to the key k
Error if key does not exist

D.get(k, default = None) Return the value corresponding to the key k


If k is not a key, it returns the default value

D[k] = v Add the k:v pair (or redefine it, if already there)

del D[k] Remove the element with key k


Error if k does not exist
D.keys() Return an iterator to the keys
D.values() Return an iterator to the values
D.items() Return an iterator to key, value pairs
k in D (k not in D) True if k is (is not) a key of D
len(D) number of key:value pairs in D
List comprehensions
[ expression for value in iterator if expression ]
• The if part might be missing
• More than one for cycle (each one eventually including
an if) can also be used

l = [n**2 for n in range(10)] à [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
[x for x in l if (x % 3) == 0] à [0,9,36,81]

range(stop): generates a sequence of


numbers from 0 to stop (not included).
More to follow…
Other comprehensions
• If {} are used instead of [], a set is created

[n%2 for n in range(10)] à [0, 1, 0, 1, 0, 1, 0, 1, 0, 1]


{n%2 for n in range(10)} à [0, 1]

• When using {}, a dict is created if the expression


includes :

{n:(n%2 == 0) for n in range(10)} à {0:True, 1: False, 2:True, 3:False,…}


Function Definition
def FunctionName(…):
def make_even(x):
“””doc string””” def add_one(x): “””return the even number >= x”””
“””just adding 1””” if x % 2 == 0:
<Block of code> y=x+1 return x
return y else
return x + 1

• Function arguments are separated by commas


• return is used to stop execution and return a value (it can be None)
• If the function ends without a return, None is returned
• The doc_string might be missing (bad idea)
• When the doc_string is defined, it is returned by help(FunctionName)
• Functions are first class objects
Positional and keyword arguments

def Function(parp1, parp2,…, parpN, park1 = X1, , park2 = X2, …, parkM = XM):

• keyword arguments follow positional arguments


• When keyword arguments are not defined at function call, the default value is used

def function(p1, p2, k1 = 2, k2 = 3):


print('p1 = ',p1,', p2 = ',p2,', k1 = ',k1,', k2 = ',k2)

function(0) à ERROR
function(0,1) à p1 = 0 , p2 = 1 , k1 = 2 , k2 = 3
function(0,1, k1 = 4) à p1 = 0 , p2 = 1 , k1 = 4 , k2 = 3
function(0,1, k2 = 5) à 1 = 0 , p2 = 1 , k1 = 2 , k2 = 5
function(0,1, k1 = 4, k2 = 5) à p1 = 0 , p2 = 1 , k1 = 4 , k2 = 5
function(k1 = 4, k2 = 5, 0, 1) à ERROR
function(k1 = 4, k2 = 5, p1 = 0, p2 = 1) à It works, but it’s horrible
Scope and Lifetime of variables
• Variables defined in the main body of the file are global
• Variables defined inside a block are local to that block
• more local variables override less local variable

def function():
def function(): 2
a=2
print(a) 1 1
print(a)
a=1
a=1
function()
function()
print(a)
a inside the
function is not the
same a as the one
outside
global
• The keyword global is needed when we want to change a global
variable inside a function
Beware: it’s not possible to refer both to a local and a
global variable with the same name
def function(): These programs raise an exception UnboundLocalError
global a 2
a=2 2
print(a) def function(): def function():
a=1 print(a) a=a+1
function() a=2 a=1
print(a) print(a) function()
a=1
Now, we’re saying that the variable function()
a inside the function is the same
variable a defined outside
*

*: expand a sequence in its components


def function(*args):
print('Number of arguments: ',len(args))
print('Arguments: ',args)

Number of arguments: 1
function(1)
Arguments: (1,)

Number of arguments: 2
function(1,2)
Arguments: (1,2)

Number of arguments: 3
function(1,2,3)
Arguments: (1,2,3)

This syntax can be used to pass an arbitrary number of positional arguments to a function
It is convention to call the sequence of arbitrary positional arguments args (but anything would
work, it’s the * making the trick)
def welcome(message,*args):
print(message)
for other_message in args:
print(other_message)

Hello !
welcome(‘Hello !’)

Hello !
welcome(‘Hello !’, ’Ciao !’, ‘Hola !’)
Ciao !
Hola !
**: expand a dictionary in its key = value components

def function(**kwargs):
print('Number of keyword arguments: ',len(kwargs))
print('Arguments: ‘,kwargs)

Number of arguments: 1
function(k1 = 1)
Arguments: {‘k1’:1}

Number of arguments: 2
function(k1 = 1, k2 = 2)
Arguments: {‘k1’:1, ‘k2’:2}

This syntax can be used to pass an arbitrary number of keyword arguments to a function
It is convention to call the sequence of arbitrary keyword arguments kwargs (but anything would
work, it’s the ** making the trick)
def welcome(message,**kwargs):
print(message)
for language, greeting in kwargs.items():
print(language,’: ‘,greeting)

welcome(‘Greetings in different languages’, uk = ‘hello’, ita = ‘ciao’, spain = ‘hola’)

Greetings in different languages


uk: hello
ita: ciao
spain: hola
Remember: Functions are objects
So they can be assigned to variables
sum_all = sum … or they can be returned by other functions
sum_all([1,2,3]) # it’s the same as sum([1,2,3])
def convert2kelvin(input_scale):
if input_scale == 'fahrenheit':
… or they can be passed to other functions
def convert(temperature):
def print_int_other_base(x, convert): return (temperature - 32) * 5/9 + 273.15
print(convert(x)) return convert
else:
print_int_other_base(21, bin) à 0b10101 def convert(temperature):
return 273.15 + temperature
print_int_other_base(21, oct) à 0o25
return convert
print_int_other_base(21, hex) à 0x15
kelvin = convert2kelvin('celsius')
kelvin(0) à 273.15
… or it’s possible to define list of functions, dictionaries kelvin = convert2kelvin('fahrenheit')
kelvin(32) à 273.15
that have functions as values or keys, etc…
Example: sort of list (iterable, key = None)

• Method that sort a list in place


• If key is defined, the elements are ordered using the
result of this function
def age(person):
return person[1]
def height(person):
return person[2]
l = [ ['mario',54,173], ['vittorio',12,145], ['bruno',74,164], ['giorgio',23,187] ]

l.sort() à [['bruno', 74, 164], ['giorgio', 23, 187], ['mario', 54, 173], ['vittorio', 12, 145]]
l.sort(key = age) à [['vittorio', 12, 145], ['giorgio', 23, 187], ['mario', 54, 173], ['bruno', 74, 164]]
l.sort(key = height) à [['vittorio', 12, 145], ['bruno', 74, 164], ['mario', 54, 173], ['giorgio', 23, 187]]
Anonymous lambda functions (functions on one-
line)

lambda variable(s): expression


It’s a method to define simple functions without name
square = lambda x: x**2 add = lambda x, y: x + y
square(10) à 100 add(1,2) à 3

l = [ ['mario',54,173], ['vittorio',12,145], ['bruno',74,164], ['giorgio',23,187] ]

l.sort() à [['bruno', 74, 164], ['giorgio', 23, 187], ['mario', 54, 173], ['vittorio', 12, 145]]
l.sort(key = lambda x: x[1]) à [['vittorio', 12, 145], ['giorgio', 23, 187], ['mario', 54, 173], ['bruno', 74, 164]]
l.sort(key = lambda x: x[2]) à [['vittorio', 12, 145], ['bruno', 74, 164], ['mario', 54, 173], ['giorgio', 23, 187]]
File Objects
file_object = open(Name_of_the_file, mode)
• Name_of_the_file is a string with the file name (full path, or with
respect to the current path)
• mode is a string that defines the kind of the file and how you want
to open the file
r Read from the beginning Error if the file does not exist t text

w Write from the beginning If the file exists, it is overwritten b binary

a Write from the end if the file does not exist, it is created

r+ Read/write from the beginning

w+ Read/write from the beginning

a+ Read/write from the end


text binary
.read([N]) Read all the characters, or a maximum of N characters if N is Read all the bytes (or N bytes
defined if defined)
.readline([N]) Read up to the new line character (included), or up to a maximum
of N characters, if N is defined
.readlines() Read all the lines and return a list of strings
.write(item) write the sequence of characters write the sequence of bytes
.writelines(list) write the elements of the list (strings)

f = open(‘file.txt’, ‘r’) x
file.txt x = f.read(1) “L”

Line 1 x = f.read(1) “i”


x = f.read(1) “n”
Line 2 x = f.read(7) “e 1\nLin”

Line 3 x = f.readline() “ea 2\n”


x = f.readline() “Linea 3\n”
Line 4 x = f.readline() “Linea 4\n”
x = readline() “”
x = read() “”
file_object.close()
• Do it after the work with the file as finished
• Remember that writing operations are not guarantee to be
synchronized until the file is closed
• To force writing to the disk, it is possible to use the method
flush()
with … as
with open(’file.txt’, ‘rt’) as fin:
s = f_in.readline()
A more compact syntax to not forget
while s: the close command
print(s)
s = f_in.readline()
Exceptions

• python as a built-in system for dealing with run-time errors


Basic syntax
try:
<Block of code>
except:
<Block of code to execute if anything went wrong in the block above>
• When catching the expression with the try statement, it is possible to
deal with the error, and to decide how the program should continue
• Exceptions that are not catched cause the program to exist
• python has many types of exceptions

ZeroDivisionError Raised when second operand of division or modulo operation is zero

ValueError Raised when a function gets argument of correct type but improper value

TypeError
Raised when a function or operation is applied to an object of incorrect type

ImportError Raised when the imported module is not found


KeyError Raised when a key is not found in a dictionary
AttributeError Raised when attribute assignment or reference fails
… …
• It is possible to choose the kind of exception to capture
try:
<Block of code>
except Exception1:
<Block of code to execute if Exception1 occurred>
• It is possible to handle different exceptions in different ways

try:
<Block of code>
except Exception1:
<Block of code to execute if Exception1 occurred>
else:
<Block executed if no exception occurred>
finally:
<Block executed in any case>
Raising exceptions
• sometimes it is useful to raise a particular exception in your
own code (so that another part of the program can handle it)
• Exceptions are raised by the raise function

def convert_kelvin_to_fahrenheit(temperature):
if (not isinstance(temperature, float)) and (not isinstance(temperature, int)):
raise TypeError('temperature is a number')
if temperature < 0:
raise ValueError('temperature in kelvin is >= 0')
return (temperature - 273.15) * 9/5 + 32
Import
import MODULE
import the entire module, objects of the module are accessible with the syntax
MODULE.object

import MODULE as NEW_MODULE_NAME


same as before, but now the module is accessible as NEW_MODULE_NAME

from MODULE import OBJECT


only the requested object is imported, and it is accessible directly as OBJECT

from MODULE import *


all the objects of the module are imported, and they are accessible with their own name
Standard library
• os, sys, shutils: tools for interacting with the operative system
• glob: search the file system
• math, cmath: some basic mathematical tools
• pickle: writing/reading objects to disk
• collections: common data structures
• time, datetime: tools to handle time-related tasks
• csv: reading/writing csv files
https://docs.python.org/3/library/
Package and Environment Managing
• ANACONDA: default package and environment manager for
python
– Different versions for python2/3 (but each one can build
environments for the other python version)
– miniconda is a lightweight version
A conda environment is a set of system variables and libraries

• conda update conda update conda to the current version


• conda env list list all the environments available
• conda create --name NAME [--clone NAME] [python=VERSION]
– create a new environment
– it is possible to choose the python version, e.g. 2.7
– with clone it is possible to inheritfrom a previous environment
• conda activate NAME activate the environment
• conda deactivate close the current environment
• conda env remove --name NAME delete the environment
• conda list List all the installed packages
• conda list WHAT List installed packages with name containing
WHAT
• conda search WHAT List available packages with name containing
WHAT
• conda install PACKAGE install the package and any dependency
• conda install PACKAGE=VERSION install that specific version of
the package
• conda uninstall PACKAGE remove it (and everything depending
on it)
• conda config --show show the entire configuration
• conda config --get channels which channels are used for
getting packages
• conda config --add channels NAME add a new channel
– the last added it he highest priority one
• conda install -c CHANNEL1 [-c CHANNEL2, …] PACKAGE
– install the package using channel1 as highest priority
Object programming
• Imperative programming: Solve the task with a sequence of commands
• Object programming: Solve the task using objects and corresponding operations

• class = an abstract model for a group of entities (persons, vehicles, random number
generators, …) à The data type
• object = an entity of a particular class à The variable
• attribute = a characteristic of the object (random number generator: mean, std, … )
• method = function offered to the outside world (random number generator: generate new
sample)
• inheritance = relationship among classes (generators of random numbers with
gaussian/uniform/… distributions are a special kind of random number generators)
class Point:
“””A point in a 2D-dimensional space”””
def __init__(self, x = 0.0, y = 0.0):
self.x = x
self.x = y

p1 = Point(2.3,6.1) # here an object of class Point is created

• The class can be named as any python object (but it is quite


common to use names starting with a capital letter)
• __init__ is the constructor of the object. It’s called every time an
object of the class Point is created
• The first argument of __init__ is always the object itself. It’s not
compulsory to call it self (but it is highly recommended)
• For the other arguments of the __init__ method, the same rules
discussed for function arguments apply
• Try help(Point) or help(p1)
• Try type(p1) or isinstance(p1, Point)
class Point: __repr__: method that is used every
“””A point in a 2D-dimensional space””” time it is necessary to convert the
def __init__(self, x = 0.0, y = 0.0): object into a string
self.x = x
self.y = y __str__: method that is used to
convert the object into a string for
printing; __repr__ is used if __str__ is
p1 = Point(2.3,6.1) not defined
print(p1) à <__main__.Point object at 0x102862be0>

class Point:
“””A point in a 2D-dimensional space”””
def __init__(self, x = 0.0, y = 0.0): self is the first
self.x = x argument of all
self.y = y the methods
def __repr__(self):
return 'x = ‘+str(self.x)+’ y = ‘+str(self.y)

p1 = Point(2.3,6.1)
print(p1) à x = 2.3 y = 6.1
• Add to the class Point a method for calculating the distance from the
origin
class Point:
“””A point in a 2D-dimensional space”””
def __init__(self, x = 0.0, y = 0.0): All the rules discussed for function
self.x = x definition apply to method definition
self.y = y
def __repr__(self):
return 'x = ‘+str(self.x)+’ y = ‘+str(self.y)
def norm(self):
return (self.x**2 + self.y**2)**0.5

p1 = Point(2.3,6.1)
print(‘distance of p1 from origin = ‘,p1.norm())
• Add a method that returns a new point rotated by teta degrees
class Point:
def __init__(self, x = 0.0, y = 0.0):
self.x = x
self.y = y
def __repr__(self):
return 'x = ‘+str(self.x)+’ y = ‘+str(self.y)
def norm(self):
return (self.x**2 + self.y**2)**0.5
def rotate(self, teta):
import math
x = math.cos(teta)*self.x - math.sin(teta)*self.y
y = math.sin(teta)*self.x + math.cos(teta)*self.y
return Point(x,y)

p1 = Point(2.3,6.1)
p2 = p1.rotate(3.14)
• Change the method rotate so that it changes the object itself, instead
of returning a new object
class Point:
def __init__(self, x = 0.0, y = 0.0):
self.x = x
self.y = y
def __repr__(self):
return 'x = ‘+str(self.x)+’ y = ‘+str(self.y)
def norm(self):
return (self.x**2 + self.y**2)**0.5
def rotate(self, teta):
import math
x = math.cos(teta)*self.x - math.sin(teta)*self.y
y = math.sin(teta)*self.x + math.cos(teta)*self.y
self.x = x
self.y = y
Remember aliasing

p1 = Point(1,2) This is a new link to the same object

p2 = p1
print('p1: ', p1) à p1: x=1y=2
print('p2: ', p2) à p2: x=1y=2 So here, we’re changing the object pointed
both by p1 and p2
p1.x = 3
print('p1: ', p1) à p1: x=3y=2
print('p2: ', p2) à p2: x=3y=2
Operator overloading
• It is possible to define how operators (+,-,==,…) work on object
of your own classes
+ __add__ unary operators
< __lt__ += __iadd__
+ __pos__
<= __le__ - __sub__
- __neg__
-= __isub__
> __gt__ abs() __abs__
* __mul__
>= __ge__ *= __imul__ int() __int__
== __eq__ / __truediv__ float() __float__
!= __ne__ /= __idiv__ round() __round__
// __floordiv__ bool()* __bool__
//= __ifloordiv
For binary operators, it is convention % __mod__
to call the second operand in the %= __imod__ * This is also used when checking if
method definition other an object is True
** __pow__
**= __ipow__
• Define the > operator of the class Point, so that p1 > p2 returns True if p1 is
further away from the origin than p2

class Point:
def __init__(self, x, y): Now sort knows what to do !
self.x = x import random
self.y = y points = [Point(random.uniform(0,1),
random.uniform(0,1)) for i in range(10)]
def __repr__(self):
points.sort()
return 'x = ‘+str(self.x)+’ y = ‘+str(self.y)
def norm(self):
return (self.x**2 + self.y**2)**0.5
def __gt__(self, other):
return self.norm() > other.norm()
• Define the == operator to check if two objects of the class Point are the same

class Point: class Point:


def __init__(self, x, y): def __init__(self, x, y):
self.x = x self.x = x
self.y = y self.y = y

p1 = Point(1,2) def __eq__(self, other):


p2 = Point(1,2) return (self.x == other.x) and (self.y == other.y)
print(p1 == p2) à False
p1 = Point(1,2)
p2 = Point(1,2)
print(p1 == p2) à True
• Define a class Polygon to represent polygon objects, where a
polygon is defined as a sequence of objects of the class Point

class Point:
def __init__(self, x, y):
self.x = x
self.y = y
class Polygon:
def __init__(self, *args):
self.vertexes = []
for vertex in args:
if not isinstance(vertex, Point):
raise TypeError()
self.vertexes.append(vertex)

triangle = Polygon(Point(0,1), Point(0,1), Point(0.5,1))


Inheritance
• New classes are created inheriting from previous classes. Only
differences between the parent class and the new class need to be
implemented
• Methods defined in the new class override methods defined in the
parent class

class New(Parent): With this syntax, the class New inherits from the class Parent

class New: Actually when the parent class is not defined, it is assumed equal to the
class object, where object is a completely generic python class
class New(object):
• Define the classes Triangle and Rectangle as classes inheriting the
class Polygon
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
class Polygon(object):
def __init__(self, *args):
self.v = []
for point in args:
self.v.append(point)
def __getitem__(self, ind):
return self.v[ind]
class Triangle(Polygon): What if I want to check that the number of
def area(self): vertexes is 3 when a triangle is defined ?
return 0.5*self.v[2].y*(self.v[1].x - self.v[0].x)
class Rectangle(Polygon):
def area(self):
return (self.v[1].x - self.v[0].x) * (self.v[2].y - self.v[0].y)
class Point(object): Here, part of the code is repeated between the
def __init__(self, x, y): __init__ method of Polygon and the __init__
self.x = x method of Triangle
self.y = y à Difficult to maintain the code
class Polygon(object):
def __init__(self, *args): But it is possible to call a method of one class
self.v = [] from any other class
for point in args:
class Triangle(Polygon):
self.v.append(point)
def __init__(self, *args):
def __getitem__(self, ind):
if len(args) != 3:
return self.v[ind]
raise ValueError()
class Triangle(Polygon):
Polygon.__init__(self, *args)
def __init__(self, *args):
if len(args) != 3:
The built-in function super() returns the parent
raise ValueError()
class
self.v = []
for point in args: class Triangle(Polygon):
self.v.append(point) def __init__(self, *args):
def area(self): if len(args) != 3:
return 0.5*self.v[2].y*(self.v[1].x - self.v[0].x) raise ValueError()
class Rectangle(Polygon): super().__init__(*args)
def area(self):
With this syntax, self does not need to be
return (self.v[1].x - self.v[0].x) * (self.v[2].y - self.v[0].y) passed as argument
enumerate(iter)
• It creates an iterator that provides the sequence of pairs
(index, value) for all the elements in object iter

l = [7,3,9] i = 0, x = 7
for i, x in enumerate(l): i = 1, x = 3
print(‘i = ‘,i,’, x = ’,x) i = 2, x = 9
range(start, stop, step)
Sequence of ordered integer numbers

l = list(range(0, 10, 2))à [0,2,3,6,8]


l = list(range(4))à [0,1,2,3]

for value in range(10): The full sequence of elements is never


actually created
print(value) Thus, it does not occupy the memory

You might also like