Langages Dynamiques

Cours 5

kn@lri.fr
http://www.lri.fr/~kn

Objets

Syntaxe des objets

Python est avant tout un langage objet. La syntaxe pour déclarer les classes est la suivante :

class Circle: def __init__(self, x, y, r): self.x = x self.y = y self.radius = r def length(self): return 2 * math.pi * self.r def area(self): return math.pi * self.r ** 2 def move(self, x, y): self.x += x self.y += y

Qu'y a t'il dans ce programme ?

On peut en suite créer des objets avec :

>>> c = Circle(1.5, 1.5, 3) >>> c.area() 28.274333882308138 >>> c.x 1.5

Contrôle d'accès

Il n'y a aucun contrôle d'accès aux éléments de la classe. Les attributs peuvent être librement lus et modifiés, il n'y a pas moyen de les marquer comme privés.

>>> c = Circle(1.5, 1.5, 3) >>> c.area() 28.274333882308138 >>> c.radius = 0 >>> c.area() 0.0 >>> c.radius = 0 >>> c.length = "foo"

On peut non seulement modifier des attributs, mais aussi en ajouter et écraser des méthodes !

Méthodes statiques

On peut utiliser un décorateur pour déclarer des méthodes comme statiques, i.e. appartenant à la classe et non pas à l'objet.

class Circle: ... # tout comme avant @classmethod def readFromFile(cls, path): with open(path) as f: line = f.readline().strip() x, y, r = line.split(",") return cls(x, y, r) #Équivalent à Circle(...)

Héritage

L'héritage s'indique assez naturellement au moment de la déclaration de la classe. Si on imagine une classe Shape dont on souhaite hériter :

class Circle (Shape): def __init__(self, x, y, r): super().__init__(self, x, y) self.radius = r ...

La fonction super() renvoie la classe parente de celle dans la quelle on se trouve. Donc super().__init__(self, ...) appelle le constructeur de la classe parente sur l'objet self que l'on est en train d'initialiser.

Héritage multiple

L'héritage multiple est possible, il suffit d'ajouter plusieurs classes parentes :

class Circle (Shape, Colored): def __init__(self, x, y, r, c): Shape.__init__(self, x, y) Colored.__init__(self, c) self.radius = r ...

Attention, si deux classes parentes possèdent les même méthodes, l'ordre de résolution est important. On peut connaître l'ordre de résolution avec la méthode .mro() (method résolution order) d'une classe.

MRO

class A: def __init__(self): pass def f(self): print ("Dans A") class B: def __init__(self): pass def f(self): print ("Dans B") class C(A, B): def __init__(self): pass >>> c = C() >>> c.f() Dans A >>> C.mro() [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]

Conclusion Objets

Compréhensions

Souvenons nous …

{ x ∈ ℕ | x mod 2 = 0 }

Notation mathématique compacte qui permet de définir l'ensemble des éléments d'un autre ensmble qui vérifient une propriété.

{ (x,y) ∈ ℝ2 | x2 + y2 = 1 }

« L'ensembles des points dont la norme vaut 1 » (i.e. le cercle de rayon 1)

C'est une notation très puissante, compacte et déclarative (elle exprime ce qu'on veut, pas comment on le calcule).

Tableaux en compréhension

Python permet de définir des tableaux en compréhension :

pairs = [ x for x in range(100) if x % 2 == 0 ] #pairs vaut [ 0, 2, 4, 6, 8, … , 98 ]

La syntaxe (simplifiée) est :

[ er for x in el if c ]

Tableaux en compréhension (2)

Python permet de définir des tableaux en compréhension :

Le code :

[ er for x in el if c ]

est équivalent à :

res = [] for x in el: if c: res = res + [er]

Exemple :

>>> [ (x-1, x+1) for x in range(10) if x % 3 = 0 ] [(-1, 1), (2, 4), (5, 7), (8, 10)] >>>

Utilité

Un tableau donné par comprhénsion permet :

C'est une opération tellement courante que les concepteurs de Python on jugé nécessaire de la mettre dans le langage.

Fonctions anonymes

Fonctions anonymes

Python supporte une syntaxe pour la déclaration de fonctions anonymes :

lambda x,y,z: x+y+z lambda x : x < 10

Ces fonctions anonymes sont des expressions. Elle permettent d'éviter la création d'une (petite) fonction et peuvent rendre le code plus lisible. Par exemple:

data = ... # tableau de dictionnaires de la forme { "annee": int, "titre": str } s_date = sorted(data, key=lambda d: d["annee"])

Ici la fonction sorted prend en argument supplémentaire une fonction permettant de dire comment extraire la clé de tri à partir d'une entrée du tableau.

Itérateurs

Les fonctions anonymes sont particulièrement utiles avec les itérateurs :map (builtins), filter (builtins), reduce (module functools)

from functools import reduce data = .... # un tableau d'entiers data_even = filter (lambda x: x % 2 == 0, data) data_square = map (lambda x: x**2, data) data_prod = reduce(lambda x, y: x * y, data, 1)

Fichiers CSV

Format CSV

On appelle format CSV tout format de fichier texte encodant des données tabulées. Avec les contraintes suivantes:

Cette définition simple est cependant approximative

Les fichiers CSV sont souvent difficile à lire quand ils ont été mal construits

Chargement de CSV en Python

Les fonctionalités liées au fichiers CSV sont dans le module csv

import csv #on veut lire un fichier "fichier.csv" contenant # Nom;Note # Toto;14 # Titi;13 # Tata;18 with open("fichier.csv", "r") as f: table = list(csv.reader(f, delimiter=';')) #table vaut #[ ['Nom', 'Note'], ['Toto', '14'], ['Titi', '13'], ['Tata', '18']]
list(csv.reader(f,delimter=c))
crée un tableau de tableaux de chaînes de caractères, à partir du descripteur de fichier f en utilisant le délimiteur c. Si le délimiteur n'est pas donné, utilise la virgule.
list(csv.DictReader(f,delimter=c))
crée un tableau de dictionnaires dont les clés sont les en-tête de colonnes.

Rappel sur les dictionnaires

Un dictionnaire (ou tableau associatif) associe des clés à des valeurs

mois = { "janvier" : 31, "février" : 28, …, "décembre" : 31 } print(mois["janvier"]) # affiche 31 mois["février"] = 29 # mise à jour del mois["novembre"] # supprime l'entrée for j in mois.values(): print(j) #affiche 31, 29, 31, … for m in mois.keys(): print(m) #affiche janvier, février, … for (m, j) in mois.items(): print(m, "a", j, "jours") #affiche janvier a 31 jours, février a …

Chargement en dictionnaires

import csv #on veut lire un fichier "fichier.csv" contenant # Nom;Note # Toto;14 # Titi;13 # Tata;18 with open("fichier.csv", "r") as f: dic_table = list(csv.DictReader(f, delimiter=';')) #dic_table vaut #[ { 'Nom':'Toto', 'Note' : '14'}, { 'Nom':'Titi', 'Note' : '13' }, … ]

Écriture d'un fichier CSV

with open("fichier2.csv", "w") as f: writer = csv.writer(f, delimiter=',') for l in table: writer.writerow(l) #Dans le cas d'un writer, on suppose que la première #ligne contient les en-tête de colonnes with open("fichier3.csv", "w") as f: writer = csv.DictWriter(f, ['Nom', 'Note'], delimiter=',') write.writeheader() #écrit les en-têtes de colonnes. for l in table: writer.writerow(l) #Dans le cas d'un DictWriter, on doit spécifier les noms de colonnes. #Une façon de faire ici serait l[0].keys() (on renvoie les clés du #dictionnaire qui est dans la première case du tableau)