Python avancé Marc de Falco Maths/Info en MPSI Centre International de Valbonne 7 mai 2013 A propos de cette présentation On parlera ici d’extensions du langage. Pour la culture plus que l’enseignement direct. Items présentés : 1 2 3 4 5 6 7 les slices les list/dict par compréhension les générateurs les décorateurs les propriétés pour les objets la surcharge d’opérateurs héritage Présentation de Python Python est un langage qui évolue : par tout petits sauts les PEP : Python Extension Proposal l’objectif est toujours le même : moins de code moins de répétition plus clair plus efficace import this : There should be one– and preferably only one –obvious way to do it. Although that way may not be obvious at first unless you’re Dutch. Les slices (1/2) Les slices permettent d’accèder à des sous listes par copie ! >>> >>> [3, >>> [0, >>> [7, >>> [0, >>> [0, >>> [9, l = [0,1,2,3,4,5,6,7,8,9] l[3:5] # l[debut:fin exclue] 4] l[:5] # debut = 0 1, 2, 3, 4] l[7:] # fin = len(l) 8, 9] l[:] # toute la liste, mais c’est une copie!!! 1, 2, 3, 4, 5, 6, 7, 8, 9] l[0:8:2] # pas 2, 4, 6] l[::-1] # pas negatif il inverse debut/fin 8, 7, 6, 5, 4, 3, 2, 1, 0] Les slices (2/2) On peut s’en servir pour faire des affectations >>> >>> >>> [0, l = list(range(10)) l[3:5] = l[7:9] l 1, 2, 7, 8, 5, 6, 7, 8, 9] # Tour de magie Pythonesque, que se passe t’il ? >>> l = list(range(10)) >>> l[::2], l[1::2] = l[1::2], l[::2] >>> l # a ne surement pas montrer aux eleves ;-) Listes par compréhension >>> [ 2 * i for i in range(5) ] [0, 2, 4, 6, 8] >>> [ [ i ** j for j in range(2,4) ] for i in range(2,4) ] [[4, 8], [9, 27]] >>> [ i for i in range(10) if i % 2 == 0 ] [0, 2, 4, 6, 8] Dictionnaires Les dictionnaires sont des tables de hachage >>> d = {} >>> d[0] = 1 >>> d[’cle’] = [1,2] >>> d {0: 1, ’cle’: [1,2]} >>> 0 in d True >>> d[0] 1 >>> d[1] [...]KeyError: 1 >>> d.get(1, -1) -1 >>> for k in d: ... print(k, d[k]) 0 1 cle [1,2] >>> d.keys() dict_keys([0, ’cle’]) >>> d.values() dict_values([1, [1, 2]]) >>> d.items() dict_items([(0, 1), (’cle’, [1, 2])]) >>> for k, v in d.items(): ... print(k, v) Dicts par compréhension >>> phrase = ’ceci est une phrase’ >>> freq = { c:s.count(c) for c in map(chr, range(ord(’a’),ord(’z’)+1)) } >>> freq {’a’: 1, ’c’: 2, ’b’: 0, ’e’: 4, ’d’: 0, ’g’: ’f’: 0, ’i’: 1, ’h’: 1, ’k’: 0, ’j’: 0, ’m’: ’l’: 0, ’o’: 0, ’n’: 1, ’q’: 0, ’p’: 1, ’s’: ’r’: 1, ’u’: 1, ’t’: 1, ’w’: 0, ’v’: 0, ’y’: ’x’: 0, ’z’: 0} 0, 0, 2, 0, ne sert pas souvent, mais parfois permet de remplacer 10 lignes moches par une jolie ligne Les générateurs (1/3) Un générateur est une fonction qui peut s’interrompre, renvoyer un résultat, puis reprendre là où elle s’était arretée Le mot clé pour s’interrompre yield. On peut naturellement utiliser un générateur comme itérable : for e in generateur: print(e) Les générateurs (2/3) 1 → 11 → 21 → 1211 → . . . import itertools def feedback(): s = ’1’ while True: yield s s = ’’.join( str(len(list(v)))+k for k,v in itertools.groupby(s) ) s = feedback() print(next(s)) print(next(s)) Les générateurs (3/3) Crible def premier(): n = 10000 crible = [ True ] * n for i in range(2,n): if crible[i]: for j in range(2,n//i): crible[i*j] = False sum(1/p for p in premier(1000)) [ p ** 2 for p in premier(10) ] Les décorateurs Un décorateur est un transformeur de fonction. @trace def fact(n): if n == 0: return 1 return n * fact(n-1) fact(3) fact fact fact fact fact fact fact fact <<<<-> -> -> -> 3 2 1 0 1 1 2 6 Les décorateurs L’envers du décor : def trace(func): def wrapped(arg): print("{} <- {}".\ format(func.__name__, repr(arg))) retour = func(arg) print("{} -> {}".\ format(func.__name__, repr(retour))) return retour return wrapped Les propriétés C’est une décoration de méthode qui permet de définir des accès types attribut : import math class Cercle: def __init__(self, rayon): self.rayon = rayon @property def aire(self): return math.pi * self.rayon ** 2 c = Cercle(5) print(c.aire) Les propriétés Pour avoir la propriété en écriture on rajoute class Cercle: (...) @aire.setter def aire(self, a): self.rayon = math.sqrt(a) / math.pi c = Cercle(5) c.aire = 100 print(c.rayon) La surcharge d’opérateurs Pour chaque opération sur les objets, il faut définir une méthode au nom prédéfini. Exemple : __add__ pour pouvoir faire o1+o2 class IntMod33: def __init__(self,n): self.n = n % 33 def __add__(self,other): return IntMod33((self.n+other.n)) o1 = IntMod33(127) o2 = IntMod33(73) print( (o1+o2).n ) La surcharge d’opérateurs Une surchage pratique : class IntMod33: (...) def __str__(self): return str(self.n) print(IntMod33(127)) Diffèrence avec __repr__. La surcharge d’opérateurs Itérer sur un objet : il suffit de définir __iter__ comme un générateur class Etsil: def __init__(self,l): self.l = list(l) def __iter__(self): for e in reversed(self.l): yield e l = Etsil(range(5)) for e in l: print(e) L’héritage Un objet peut hériter d’un autre objet. Syntaxe : class Fille(Mere): Tous les attributs/méthodes non redefinis sont hérités. On peut faire référence aux méthodes de sa mère. Syntaxe : super(Fille,self) soit en tant que Mere L’héritage class A: def f(self): return 3 class B(A): def f(self): return 1 + super(B,self).f() b = B() print(b.f()) L’héritage multiple L’approche Pythonesque : mixins class PossedeValeur: def __init__(self,v): self.valeur = v class AfficheValeur(PossedeValeur): def __str__(self): return str(self.valeur) class AjouteValeur(PossedeValeur): def __add__(self,o): # ici type(self) est le meilleur type return type(self)(o.valeur+self.valeur) class Mixin(PossedeValeur, AfficheValeur,AjouteValeur): pass m = Mixin(42) print(m+m) Il en reste... Principalement les meta-classes qui permettent de fabriquer des classes. Exemple en django (framework web) : Une classe pour décrire une relation du modèle relationnel Une meta-classe en déduit une classe de manipulation sur la base de donnée automatiquement