Mickaël Tits CETIC mickael.tits@cetic.be
Comme dans la plupart des langages de programmations, lorsqu'une instruction ne peut être correctement effectuée, l'interpréteur renvoie un message d'erreur, et s'arrête. On appelle ça une exception. Le message d'erreur est en général une source d'information importante pour le développeur, car il permet de comprendre le bug et ainsi de le résoudre.
On peut cependant contourner une exception pour permettre au programme de continuer, en définissant un comportement spécifique à adopter lorsqu'une exception survient.
#Exemple d'exception
x = 0
inverse = 1/x
x = 0
try:
inverse = 1/x
except:
inverse = float("inf")
print(inverse)
try:
inverse = 1/x
except Exception as e:
print(e)
inverse = float("inf")
print(inverse)
try:
inverse = 1/y
except ZeroDivisionError as e:
inverse = float("inf")
print(inverse)
def my_function(x1, x2):
my_function
est l'identifiant de la fonction, et les éléments entre parenthèsex1, x2
sont les arguments de la fonction.
return
permet de renvoyer un résultat de la fonction et de sortir de la fonction. Ce mot-clé peut être utilisé plusieurs fois (par exemple pour retourner un résultat différent selon une condition).Exemple:
def my_function(x1, x2):
out = x1*x2
return out
On peut ensuite appeler une fonction de cette manière:
a = 6
b = 7
output = my_function(a, b)
Les variables a
et b
sont passées en arguments de la fonction my_function
, et le résultat de la fonction (42) est renvoyé. La variable output
est donc assignée à 42
.
def factorial(n):
"""
Cette fonction permet de calculer le factoriel de n
"""
f = 1
for i in range(1,n+1):
f = f * i
return f
print(factorial(2), factorial(3), factorial(20) )
x = 1
y = 2
def my_function():
print('x inside function =', x) #utilise la variable définie en-dehors de la fonction
try:
print(y)
except Exception as exc:
print(exc)
#print(y) #renvoie une erreur car une fonction locale du même nom est déclarée après
y = 4
print('y inside function =', y) #imprime la variable locale
z = 3
print('z inside function =', z) #imprime la variable locale
my_function()
print('x outside function =', x)
print('y outside function =', y)
try:
print(z)
except Exception as exc:
print(exc)
#print(z) # renvoie une erreur car la variable n'est pas définie en-dehors de la fonction
def my_function(param1, param2 = "param2", param3 = "param3", param4 = "param4"):
print(param1, param2, param3, param4)
try:
my_function()
except Exception as e:
print("Exception:", e)
my_function(1)
my_function(1, 2)
my_function(1, param3 = 2)
def square(x):
return x**2
def cube(x):
return x**3
my_list = [square, cube]
operands = [4,5]
operations = [my_list, operands]
print(operations)
print( [[func(op) for func in operations[0]] for op in operations[1]])
Les types de base, tel que les int, float, string, bool
sont immuables. Comme vu précédemment, les tuple
sont également immuables. Immuable signifie qu'après leur création, on ne peut pas modifier les objets en mémoire. C'est pourquoi le code suivant donne une erreur :
my_tuple = (1,2,3)
my_tuple[0] = 42
my_int1 = 1
#On crée une nouvelle étiquette (référence) vers l'objet 1
my_int2 = my_int1
#la fonction id renvoie l'adresse mémoire de l'objet (i.e. des données réellement stockées dans la mémoire RAM de l'ordinateur!)
#Rien ne sert de stocker plusieurs fois l'entier 1 en mémoire, c'est pourquoi les deux variables renvoie au même endroit.
print( my_int1, my_int2)
print( id(my_int1) , id(my_int2) )
#L'objet entier est immuable, donc lorsqu'on veut modifier my_int2, l'objet 1 n'est pas modifié en mémoire. A la place, l'étiquette "my_int2" est réassignée vers un nouvel objet en mémoire (en l'occurrence un objet de type entier et de valeur égale à 2)
my_int2 += 1
print( my_int1, my_int2)
print( id(my_int1) , id(my_int2) )
#Les tuples sont plus faciles (et rapides) à assigner
%timeit my_tuple = (1,2,3, 4, True, "hello" )
%timeit my_list = [1,2,3, 4, True, "hello" ]
#Les tuples sont plus légers que les listes
import sys
my_tuple = (1,2,3, 4, True, "hello" )
my_list = list(my_tuple)
print(sys.getsizeof( my_tuple ) , sys.getsizeof( my_list ) )
A l'inverse, les list, dict, set
sont mutables, c'est-à-dire qu'on peut modifier l'objet lui-même.
Attention: lorsqu'on assigne une variable avec un objet mutable existant, cette variable devient une référence vers le même objet en mémoire! Ainsi, le code ci-dessous va modifier à la fois les variables my_list1
et my_list2
:
my_list1 = [1,2,3]
my_list2 = my_list1
my_list2[0] = 42
my_list1.append(4)
my_list2 += [5]
print(my_list1, my_list2)
#L'opérateur "=" réassigne my_list2, ce qui crée donc une nouvelle liste (différente de my_list1)
my_list2 = my_list1+[6]
my_list2 += [7]
print(my_list1, my_list2)
Lorsqu'on passe une variable en argument dans une fonction, c'est la référence de l'objet lui-même qui est passée à la fonction et non une copie (contrairement au langage C par exemple). ainsi, si l'objet passé en argument est mutable, celui-ci peut être modifié dans la fonction:
def modify(list_argument, int_argument):
list_argument+=[4]
int_argument += 4
print("inside:", list_argument, int_argument)
my_int = 2
my_list = [1,2,3]
modify(my_list, my_int)
#Un objet mutable est modifié en-dehors de la fonction, pas un objet immuable (puisque par définition ile ne peut être modifié)
print("outside:", my_list, my_int)
my_float = 4.2
help(my_float)
#Attributs d'un float
print(my_float.real, my_float.imag)
#Une méthode d'un float
my_float = 4.0
my_float.is_integer()
print(dir(my_list))
my_list.index(2, 5)
Les classes permettent d'encapsuler les différents attributs et méthodes dans une seule entité. L'encapsulation du code permet de rendre le code plus clair, et plus modulaire:
class House:
"""
Cette classe permet de définir les propriétés d'une maison, donc l'adresse, le prix, la surface et le nombre de chambre.
Elle permet de vérifier la validité desdonnées (validate()), d'afficher les informations sur la maison (display()), et de calculer le prix par m2 (compute_price_m2())
"""
def __init__(self, address, price, surface, rooms):
self.address = address
self.price = price
self.surface = surface
self.rooms = rooms
#On vérifie la validité des données, et on les rend valides si possible, sinon on renvoie False (pas valide)
self.is_valid = self.validate()
def compute_price_m2(self):
return self.price/self.surface
def validate(self):
"""
Vérifie si l'objet est valide
"""
#Si le prix, la surface, ou le nombre de chambre ne sont pas des int, on essaye de les convertir, sinon pas valide
try:
self.price = int(self.price)
self.surface = int(self.surface)
self.rooms = int(self.rooms)
return True
except:
return False
def display(self):
if self.is_valid:
print(self.address, ":", self.price, "€, ", self.surface, "m2", self.rooms, "rooms")
else:
print("L'élément n'est pas un élément valide:", self.address)
house1 = House("Rue de Bruxelles 42, Namur", 300000, 120, 3)
house1.display()
#appeler un attribut (pas de parenthèses)
print(house1.price)
#appeler une méthode (parenthèses nécessaires)
print(house1.compute_price_m2())
Remarque: On pourrait définir chaque méthode comme une fonction en-dehors du cadre de la classe, mais ça n'a pas d'intérêt puisque le processus est spécifique à la classe House. De plus, ça rend le code moins clair et rend possible une utilisation non-conforme, ce qui amène à des bugs:
def display(my_house):
if my_house.is_valid:
print(my_house.address, ":", my_house.price, "€, ", my_house.surface, "m2", my_house.rooms, "rooms")
else:
print("L'élément n'est pas un élément valide:", my_house.address)
display(house1)
display([1,2,3])
#Attention, par défaut les classes sont mutables
house2 = house1
house2.price = 350000
print(house1.price)
#pour créer une copie d'un objet mutable, on peut utiliser un module Python: copy.copy
from copy import copy
house2 = copy(house1)
house2.price = 500000
print(house1.price)
Remarque: On peut tout à fait ajouter un attribut à un objet créé à partir d'une classe. Ce n'est cependant pas conseillé car les objets d'une même classe perdent alors leur homogénéité.
house2.floors = 2
print(house2.floors)
#L'hétérogénéité des objets d'une même classe rendent le programme plus compliqué, diminuent la clarté et augmentent les risques de bugs
print(house1.floors)
Maintenant que vous connaissez les concepts principaux du langage Python, vous pouvez passez au Chapitre 4: Un exemple concret: analysons quelques biens immobiliers...