= Python : Atelier intermédiaire = <> ---- == INTRODUCTION == * Objectifs : * utiliser la bibliothèque standard et des packages existants * savoir coder ses propres objets ---- == STANDARD LIBRARY ET PACKAGES == === Bibliothèque standard (standard library) === Modules explorés ou abordés pendant ateliers AUF : * datetime * pickle * sqlite3 * re Modules potentiellement utiles pour administrateurs système : * subprocess {{{ import subprocess out = subprocess.call("grep -rsn 'davin' . > ~/Bureau/davin", shell=True) print out resultat = subprocess.Popen("grep -rsn 'davin' .", shell=True, stdout=subprocess.PIPE).communicate()[0] type(resultat) print resultat }}} * shell=True pour commande juste en un string * pwd {{{ # http://www.python.org/doc/lj21.html import posix import pwd pwd.getpwall() print 'hello', pwd.getpwuid(posix.getuid())[0] # progfou: et autre détail non négligeable pour nous à l'AuF : pwd fait des appels à la libc, qui passe par NSS, donc ça donne aussi les comptes réseau }}} * socket {{{ # socket.getaddrinfo pour faire de la résolution DNS import socket socket.getaddrinfo('smtp.sn.auf.org', 25) }}} * os.path * sys Autres modules d'intérêt : * os.system * zlib * csv * json * email * uuid * urllib * gettext * pdb === Packages === pypi * http://pypi.python.org/ * numpy, scipy {{{ # fonctions d'optimisation def f(x) : return x**3 - x**2 -10 from scipy import optimize optimize.fsolve(f, 1) }}} [[http://dakarlug.org/pat/scientifique/html/|Python African Tour (Dakar, 2009) : Informatique scientifique avec Python]] * android * [[http://forum.frandroid.com/topic/1519-tuto-programmer-en-python-v26-sur-un-telephone-android/|Tutoriel en français]] (2009) * nltk * jabberbot * django http://pypi.auf.org/ * auf.django.references autres * [[http://www.slideshare.net/ffunction/fabric-cuisine-and-watchdog-for-server-administration-in-python|Fabric, Cuisine, Watchdog]] * [[http://code.google.com/p/pyo/|pyo]] {{{ from pyo import * s = Server().boot() s.start() wav = SquareTable() env = CosTable([(0,0), (100,1), (500,.3), (8191,0)]) met = Metro(.125, 12).play() amp = TrigEnv(met, table=env, mul=.1) pit = TrigXnoiseMidi(met, dist='loopseg', x1=20, scale=1, mrange=(48,84)) out = Osc(table=wav, freq=pit, mul=amp).out() s.stop() }}} == CLASSES ET OBJETS == En Python, tout est un objet... on veut donc pouvoir créer nos propres objets en Python. === Classe et objets : structure et données === * Les classes sont une structure, un type de données * Les objets sont les données, les variables de ce type * Schéma : * Monde : objets réels (ou concepts) * Modélisation du domaine : conceptualisations, (pseudo) UML * Modélisation informatique : '''classes''' * Données : '''objets''' virtuels === Attributs et méthodes === * La classe est du code informatique qui * structure les données : '''attributs''' ''un peu comme une table de DB a une structure : colonnes'' * déclare des traitements possibles sur ces données : '''méthodes''' (fonctions dans classe) ''un peu comme une DB a traitement informatique possible sur données : procédures stockées'' * '''méthodes''' : fonctions, donc callable * '''attributs''' : variables * nom des attributs et méthodes : visible dans introspection === Créer une Classe === {{{ class Personne(): pass }}} * class * convention de nommage : CamelCase * PersonneMorale : ok * personne_morale : pas ok === self === * représente l'objet ''en cours'' dans le code de la Classe * nom générique : veut dire "quelque soit l'objet ''en cours'', voici ce que je veux faire de '''cet''' objet" * équivalent à '''this''' en Java ou PHP === Créer méthode d'initialisation d'objet === {{{ class Personne(): def __init__(self, nom, prenom=""): self.nom = nom self.prenom = prenom }}} * self : toujours comme premier argument * autres paramètres reçus lors d'initialisation (nom, prenom) sont stockés sur l'objet * les attributs 'nom' et 'prenom' sur l'objet : self.nom, self.prenom, sont des variables avant une valeur propre à l'objet === Créer une (autre) méthode === {{{ class Personne(): def __init__(self, nom, prenom=""): self.nom = nom self.prenom = prenom def nom_complet(self): return "%s %s" % (self.nom.upper(), self.prenom) }}} * Convention de nommage : lower_case * NomComplet : pas ok * nom_complet : ok * self : premier paramètre * tout le reste : c'est simplement une fonction (paramètres en input, traitement, return pour output) === CRUD d'objets === * '''Create''' : création d'objet {{{ davin = Personne(nom="Baragiotta", prenom="Davin") }}} On utilise la classe pour créer un objet de cette classe. On passe en paramètre les infos nécessaires à {{{__init__()}}} pour créer l'objet. * vérification des attributs de l'objet davin {{{ davin. [+ tab] # ipython davin.nom davin.prenom }}} * '''Update''' : modification de l'objet {{{ davin.prenom = "Daniel" davin.prenom }}} * '''Delete''' : suppression de l'objet {{{ del davin }}} * '''Retrieve''' : récupération de l'objet n'a pas de sens si on a de la permanence de l'information. Typiquement, on voudra stocker nos objets dans des bases de données en liant nos Classes à des tables. C'est ce que les ORM (Object-relational-mapper) font. Atelier Django montre comment récupérer les objets stockés dans DB. === Attributs et méthodes : de classe et d'objet === Dans l'exemple précédant, nous avons vu les attributs et méthodes d'un '''objet''' : * objet.attribut {{{ davin.nom }}} * objet.methode() {{{ davin.nom_complet() }}} Il existe aussi des attributs et méthodes de '''classe''' : {{{ class Personne(): """Représente les personnes""" definition = "Une personne est un individu physique, un humain quoi!" def __init__(self, nom, prenom=""): self.nom = nom self.prenom = prenom def nom_complet(self): return "%s %s" % (self.nom.upper(), self.prenom) @staticmethod def wordnet_synonyme(): wordnet_syn = "Individual" # fake ;) return wordnet_syn }}} * Classe.attribut {{{ Personne.definition }}} * Classe.methode() {{{ Personne.wordnet_synonyme() }}} Il est à noter que : * l'attribut de '''classe''' est déclaré directement sous la classe... ... pas dans la méthode {{{__init__()}}} : car cet attribut n'est pas propre à un objet * la méthode de '''classe''' ne prend pas l'argument : '''self''' * pour déclarer une méthode de classe, il faut utiliser le '''décorateur''' @staticmethod * tous les '''objets''' d'une classe ont accès aux attributs et méthodes (génériques) de la '''classe'''. * la classe, par contre, n'a accès qu'a ses propres attributs et méthodes. {{{ davin = Personne(nom="Baragiotta", prenom="Davin") pascal = Personne(nom="Bou Nassar", prenom="Pascal") davin.definition 'Une personne est un individu physique, un humain quoi!' pascal.definition 'Une personne est un individu physique, un humain quoi!' Personne.definition 'Une personne est un individu physique, un humain quoi!' davin.definition == pascal.definition == Personne.definition True davin.wordnet_synonyme() 'Individual' pascal.wordnet_synonyme() 'Individual' In [11]: Personne.nom --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) /home/giotta/Bureau/ in () AttributeError: class Personne has no attribute 'nom' In [12]: Personne.prenom --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) /home/giotta/Bureau/ in () AttributeError: class Personne has no attribute 'prenom' In [13]: Personne.nom_complet() --------------------------------------------------------------------------- TypeError Traceback (most recent call last) /home/giotta/Bureau/ in () TypeError: unbound method nom_complet() must be called with Personne instance as first argument (got nothing instead) }}} === Héritage === On peut créer une hiérarchie de classe, avec des SousClasse qui hérite des ClasseParent. ==== Syntaxe d'héritage ==== * On déclare les classes héritées en paramètre de la classe (entre les parenthèses de la définition de classe). * La classe suprême en Python est la classe '''object'''. * On peut donc faire hériter nos classes de cet objet au sommet de la hiérarchie {{{ class Personne(object): pass class Chat(): pass Chat. [+tab] # ipython Personne. [+tab] # ipython }}} * dans cet exemple, on voit que Personne a plus de noms (attributs, méthodes) dans son espace de nom... * ... Personne a hérité des attributs et méthodes de '''object''' ==== Attributs et méthodes de ClasseParent sont hérités ==== * Une sous-classe hérite donc des attributs et méthodes de sa classe parent {{{ class Personne(object): """Représente les personnes""" definition = "Une personne est un individu physique, un humain quoi!" def __init__(self, nom, prenom=""): self.nom = nom self.prenom = prenom def nom_complet(self): return "%s %s" % (self.nom.upper(), self.prenom) @staticmethod def wordnet_synonyme(): wordnet_syn = "Individual" # fake ;) return wordnet_syn class Professeur(Personne): pass Personne. [+tab] # ipython Professeur. [+tab] # ipython }}} ==== __init__() d'une SousClasse ==== * si une classe hérite : * son {{{__init__()}}} '''doit''' appeler {{{__init__()}}} de son parent * en passant bien tous les arguments requis par la ClasseParent... * ... incluant '''self''' {{{ class Personne(): """Représente les personnes""" definition = "Une personne est un individu physique, un humain quoi!" def __init__(self, nom, prenom=""): self.nom = nom self.prenom = prenom def nom_complet(self): return "%s %s" % (self.nom.upper(), self.prenom) @staticmethod def wordnet_synonyme(): wordnet_syn = "Individual" # fake ;) return wordnet_syn class Professeur(Personne): def __init__(self, nom, prenom="", universite=""): self.universite = universite Personne.__init__(self, nom, prenom) In [2]: davin = Personne(nom="Baragiotta", prenom="Davin") In [3]: prof = Professeur(nom="Lemay", prenom="Paul", universite="UQAM") In [4]: prof. prof.__class__ prof.__module__ prof.nom_complet prof.wordnet_synonyme prof.__doc__ prof.definition prof.prenom prof.__init__ prof.nom prof.univesite In [5]: davin. davin.__class__ davin.__module__ davin.nom_complet davin.__doc__ davin.definition davin.prenom davin.__init__ davin.nom davin.wordnet_synonyme }}} == AUTRES NOTIONS PYTHON == === Décorateurs === Les décorateurs sont des fonctions qui "wrappent" d'autres fonctions : * ils permettent notamment de contrôler l'accès aux fonctions wrappées * mais pourraient aussi modifier input et output de ces fonctions Exemple de décorateur conçu pour contrôler l'accès : {{{is_admin}}} {{{ def is_admin(fn): def inner(admin, *args, **kwargs): if admin: return fn(*args, **kwargs) else : return "Nope" return inner @is_admin def salut(nom): return "salut %s" % nom }}} Pour décorer une fonction : décorer avec {{{@nom_fonction_qui_decore}}} Il faut appeler la fonction décorée aussi avec les paramètres requis par la fonction qui décore. {{{ In [10]: admin = True In [11]: salut(admin, 'Davin') Out[11]: 'salut Davin' In [12]: admin = False In [13]: salut(admin, 'Davin') Out[13]: 'Nope' }}} === Exceptions === Pour gérer les exceptions qui peuvent être levées lors de l'exécution du code... ... il faut utiliser la structure du langage : '''try''' et '''except''' {{{ In [18]: davin = Personne(nom="Baragiotta", prenom="Davin") In [19]: davin.nationalite --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) /home/giotta/Bureau/ in () AttributeError: Personne instance has no attribute 'nationalite' In [20]: try: ....: davin.nationalite ....: except AttributeError: ....: davin.nationalite = None ....: ....: In [21]: davin.nationalite In [22]: }}} * après le mot '''except''' on fournit le type d'exception qu'on veut gérer, ex.: {{{AttributeError}}} ---- == EXERCICE == * créer une classe Personne * créer une méthode nom_complet() (ou fullname() ) sur la classe Personne retournant le nom complet d'un personne * créer une classe Professeur qui hérite de Personne * créer un attribut "formations" pour les objets de la classe Professeur : c'est la liste des formations que donne le prof * tester comment ajouter ou supprimer une formation à un professeur : quelle approche la plus simple? * créer une méthode Professeur.ajout_formation() ? * ou simplement exploiter les méthodes de liste : Professeur.formations.append() ? ---- == CONCLUSION == * ne pas réinventer la roue : utiliser l'existant * DIY (do-it-yourself) : pour le reste, faites du "sur mesure" * yalla!