Programmation orientée objet dans le langage python cours

Table des matières
1. Introduction 3
2. Objet et caractéristiques4
2.1. Ilaunedrôledetêtecetype-là . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.2. Montre-moitesattributs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.3. Discoursdelaméthode. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
3. Classes 8
3.1. LaclasseàDallas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
3.2. Argumentonspourconstruire . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.3. Commentveux-tuquejet’encapsule ? . . . . . . . . . . . . . . . . . . . . . . . 10
3.3.1. Aucommencementétaientlesinvariants . . . . . . . . . . . . . . . . . . 10
3.3.2. Protège-moi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.4. Tuaimeslesglaces,canard ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3.5. TP :Forum,utilisateursetmessages . . . . . . . . . . . . . . . . . . . . . . . . 13
4. Extension et héritage 15
4.1. Hériterentoutesimplicité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4.1.1. Sous-typage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
4.2. Laredéfinitiondeméthodes,c’estsuper ! . . . . . . . . . . . . . . . . . . . . . 17
4.3. Uneclasseavecdeuxmamans . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
4.3.1. Ordred’héritage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
4.3.2. Mixins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.4. TP :Filsdediscussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
5. Opérateurs 26
5.1. Desméthodesunpeuspéciales . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
5.2. Douxopérateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.2.1. Opérateursarithmétiques . . . . . . . . . . . . . . . . . . . . . . . . . . 28
5.2.2. Opérateursarithmétiquesunaires . . . . . . . . . . . . . . . . . . . . . . 29
5.2.3. Opérateursdecomparaison . . . . . . . . . . . . . . . . . . . . . . . . . 30
5.2.4. Autresopérateurs. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
5.3. TP :Arithmétiquesimple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
6. Programmation orientée objet avancée34
6.1. Lesattributsentrentenclasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
6.2. Laméthodepouravoirlaclasse . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
6.3. Lestatiquec’estfantastique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
6.4. Attributes-tulà ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
6.5. Ladynamiquedespropriétés. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
6.6. L’artdesclassesabstraites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Table des matières
6.7. TP :Basededonnées . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
7. Conclusion 46
1. Introduction
Plusqu’unsimplelangagedescript,Pythonestaussiunlangageorientéobjet.
Celangagemoderneetpuissantestnéaudébutdesannées1990sousl’impulsiondeGuidovan Rossum.
Apparue dans les années 60 quant à elle, la programmation orientée objet (POO) est un paradigmedeprogrammation ;c’est-à-direunefaçondeconcevoirunprogrammeinformatique, reposantsurl’idéequ’unprogrammeestcomposéd’objetsinteragissantlesunsaveclesautres.
Endéfinitive,unobjetestunedonnée.Unedonnéeconstituéedediversespropriétés,etpouvant êtremanipuléepardifférentesopérations.
Laprogrammationorientéeobjetestleparadigmequinouspermetdedéfinirnosproprestypes d’objets,avecleurspropriétésetopérations.Ceparadigmevientavecdenombreuxconceptsqui serontexplicitéslelongdececours.
Àtraverscetutoriel,nousallonsnousintéresseràcettefaçondepenseretdeprogrammeravec lelangagePython.
Ilvousestconseillédemaîtriserlesbasesdecedernier(manipulationdevaleurs,structuresde contrôle,structuresdedonnées,fonctions)avantdecontinuervotrelecture.
Noustravailleronsiciaveclaversion3dePython(version3.4ousupérieureconseillée).
2. Objet et caractéristiques
Toutd’abord,qu’est-cequ’unobjet ?Enintroduction,nousavonsditqu’unobjetavaitdes propriétésetpouvaitêtremanipulépardesopérations,c’estbienbeautoutçamaisqu’est-ce queçapeutbienvouloirdireconcrétement ?
En introduction, nous avons décrit un objet comme une valeur possédant des propriétés et manipuléepardesopérations.
Concrètement,unobjetestconstituéde3caractéristiques :
— Un type, qui identifiele rôle de l’objet (int, str et list sontdes exemples de types d’objets) ;
— Desattributs,quisontlespropriétésdel’objet ;
— Desméthodes,lesopérationsquis’appliquentsurl’objet.
Pourvouséclairer,prenonslecodesuivant :
1 >>> number = 5 # On instancie une variable `number` de type `int`
2 >>> number.numerator # `numerator` est un attribut de `number`
3 5
4 >>> values = [] # Variable `values` de type `list`
5 >>> values.append(number) # àppend` est une méthode de `values`
6 >>> values
7 [5]
ToutevaleurenPythonestdoncunobjet.
2.1. Il a une drôle de tête ce type-là
Ainsi,toutobjetestassociéàuntype.Untypedéfinitlasémantiqued’unobjet.Onsaitpar exemplequelesobjetsdetypeintsontdesnombresentiers,quel’onpeutlesadditionner,les soustraire,etc.
Pour la suite de ce cours, nous utiliserons un type User représentant un utilisateur sur un quelconquelogiciel.Nouspouvonscréercenouveautypeàl’aideducodesuivant :
Nous reviendrons sur ce code par la suite, retenez simplement que nous avons maintenant à notredispositionuntypeUser.
PourcréerunobjetdetypeUser,ilnoussuffitdeprocéderainsi :
OnditalorsquejohnestuneinstancedeUser.
2.2. Montre-moi tes attributs
Ensuite,nousavonsditqu’unobjetétaitconstituéd’attributs.Cesderniersreprésententdes valeurspropresàl’objet.
pass | word |
NosobjetsdetypeUserpourraientparexemplecontenirunidentifiant(id),unnom(name)et unmotdepasse().
EnPython,nouspouvonsfacilementassocierdesvaleursànosobjets :
Lecodeci-dessusaffiche :
Nousavonsinstanciéunobjetnomméjohn,detypeUser,auquelnousavonsattribuétrois attributs.Puisnousavonsaffichélesvaleursdecesattributs.
Notezquel’onpeutredéfinirlavaleurd’unattribut,etqu’unattributpeutaussiêtresupprimé àl’aidedel’opérateurdel.
1 >>> john.password = 'mot de passe plus sécurisé !'
2 >>> john.password
3 'mot de passe plus sécurisé !'
4 >>> del john.password
5 >>> john.password
6 Traceback (most recent call last):
7 File "<stdin>", line 1, in <module>
8 AttributeError: 'User' object has no attribute 'password'
!
Ilestgénéralementdéconseillédenommerunevaleurdelamêmemanièrequ’unefonction built-in.Onéviteraparexempled’avoirunevariableid,typeoulist.
Danslecasd’unattribut,celan’estpasgênantcarnefaitpaspartiedumêmeespacede noms.Eneffet,’entrepasenconflitavecid.
2.3. Discours de la méthode
Enfin,lesméthodessontlesopérationsapplicablessurlesobjets.Cesontenfaitdesfonctions quirecoiventnotreobjetenpremierparamètre.
NosobjetsUsernecontiennentpasencoredeméthode,nousdécouvrironscommentenajouter dans le chapitre suivant. Mais nous pouvons déjà imaginer une méthode check_pwd (check password) pour vérifier qu’un mot de passe entré correspond bien au mot de passe de notre utilisateur.
ap | pend |
Les méthodes recevant l’objet en paramètre, elles peuvent en lire et modifier les attributs. Souvenez-vousparexempledelaméthodedeslistes,quipermetd’insérerunnouvel élément,ellemodifiebienlalisteenquestion.
Àtraverscettepartienousavonsdéfinietexplorélanotiond’objet.
Un terme a pourtant été omis, le terme « classe ». Il s’agit en Python d’un synonyme de
« type ».Unobjetétantlefruitd’uneclasse,ilesttempsdenousintéresseràcettedernièreet àsaconstruction.
3. Classes
Uneclasseestenfaitladéfinitiond’,strouencorelistsontdesexemplesde classes.Userenestuneautre.
Une classe décrit la structure des objets du type qu’elle définit : quelles méthodes vont être applicablessurcesobjets.
3.1. La classe à Dallas
Ondéfinituneclasseàl’aidedumot-clefclasssurvoléplustôt :
Mon | Nom | De | Classe |
(l’instructionpassserticiàindiqueràPythonquelecorpsdenotreclasseestvide) IlestconseilléenPythondenommersaclasseen CamelCase,c’estàdirequ’unnomestcomposé d’une suite de mots dont la première lettre est une capitale. On préférera par exemple une classequemon_nom_de_classe.Exceptionfaitedestypes builtins quisont courammentécritsenlettresminuscules.
Oninstancieuneclassedelamêmemanièrequ’onappelleunefonction,ensuffixantsonnom d’unepairedeparenthèses.CelaestvalablepournotreclasseUser,maisaussipourlesautres classesévoquéesplushaut.
LaclasseUserestidentiqueàcelleduchapitreprécédent,ellenecomporteaucuneméthode. Pourdéfiniruneméthodedansuneclasse,ilsuffitdeprocédercommepourunedéfinitionde fonction,maisdanslecorpsdelaclasseenquestion.
NotrenouvelleclasseUserpossèdemaintenantuneméthodecheck_pwdapplicablesurtous sesobjets.
Quelestceselfreçuenpremierparamètreparcheck_pwd ?Ils’agitsimplementdel’objet sur lequel on applique la méthode, comme expliqué dans le chapitre précédent. Les autres paramètresdelaméthodearriventaprès.
User.check_pwd(john, | '12345') |
Laméthodeétantdéfinieauniveaudelaclasse,ellen’aquecemoyenpoursavoirquelobjet estutilisé.C’estuncomportementparticulierdePython,maisretenezsimplementqu’appeler john.check_pwd('12345') équivaut à l’appel. C’est pourquoijohncorrespondraiciauparamèreselfdenotreméthode.
selfn’estpasunmot-clefdulangagePython,leparamètrepourraitdoncprendren’importe quelautrenom.Maisilconserveratoujourscenomparconvention.
pass | word | word |
Notezaussi,danslecorpsdelaméthodecheck_pwd,que et sont biendeuxvaleursdistinctes :lapremièreestleparamètrereçuparlaméthode,tandisquela secondeestl’attributdenotreobjet.
3.2. Argumentons pour construire
Nous avons vu qu’instancier une classe était semblable à un appel de fonction. Dans ce cas, commentpasserdesargumentsàuneclasse,commeonleferaitpourunefonction ?
Il faut pour cela comprendre les bases du mécanisme d’instanciation de Python. Quand on appelleuneclasse,unnouvelobjetdecetypeestconstruitenmémoire,puisinitialisé.Cette initialisationpermetd’assignerdesvaleursàsesattributs.
L’objetestinitialiséàl’aided’uneméthodespécialedesaclasse,laméthode__init__.Cette dernièrerecevralesargumentspasséslorsdel’instanciation.
Nousretrouvonsdanscetteméthodeleparamètreself,quiestdoncutilisépourmodifierles attributsdel’objet.
3.3. Comment veux-tu que je t’encapsule ?
3.3.1. Au commencement étaient les invariants
Lesdifférentsattributsdenotreobjetformentunétatdecetobjet,normalementstable.Ilssont eneffetliéslesunsauxautres,lamodificationd’unattributpouvantavoirdesconséquencessur unautre.Lesinvariantscorrespondentauxrelationsquilientcesdifférentsattributs.
pass | word |
ImaginonsquenosobjetsUsersoientdotésd’unattributcontenantuneévaluationdumotde passe(savoirsicemotdepasseestassezsécuriséounon),ildoitalorsêtremisàjourchaque foisquenousmodifionsl’attributd’unobjetUser.
Danslecascontraire,lemotdepasseetl’évaluationneseraientpluscorrélés,etnotreobjet Userneseraitalorsplusdansunétatstable.Ilestdoncimportantdeveilleràcesinvariants pourassurerlastabilitédenosobjets.
3.3.2. Protège-moi
Auseind’unobjet,lesattributspeuventavoirdessémantiquesdifférentes.Certainsattributs vontreprésenterdespropriétésdel’objetetfairepartiedesoninterface(telsqueleprénomet lenomdenosobjetsUser).Ilspourrontalorsêtrelusetmodifiésdepuisl’extérieurdel’objet, onparledanscecasd’attributspublics.
D’autresvontcontenirdesdonnéesinternesàl’objet,n’ayantpasvocationàêtreaccessibles depuisl’extérieur.Nousallonssécurisernotrestockagedumotdepasseenajoutantuneméthode pourle hasher1 (àl’aidedumodulecrypt),afindenepasstockerd’informationssensiblesdans l’objet.Cecondensatdumotdepassenedevraitpasêtreaccessibledel’extérieur,etencore moinsmodifié(cequienaltéreraitlasécurité).
Delamêmemanièrequepourlesattributs,certainesméthodesvontavoiruneportéepubliqueet d’autresprivée(onpeutimagineruneméthodeinternedelaclassepourgénérernotreidentifiant unique).Onnommeencapsulationcettenotiondeprotectiondesattributsetméthodesd’un objet,danslerespectdesesinvariants.
Certainslangages2 implémententdansleursyntaxedesoutilspourgérerlavisibilitédesattributs etméthodes,maisiln’yariendetelenPython.Ilexisteàlaplacedesconventions,quiindiquent auxdéveloppeursquelsattributs/méthodessontpublicsouprivés.Quandvousvoyezunnom d’attributouméthodedébuterparun_auseind’unobjet,ilindiquequelquechosed’interneà l’objet(privé),dontlamodificationpeutavoirdesconséquencesgravessurlastabilité.
_pass | word |
Onnotetoutefoisqu’ilnes’agitqued’uneconvention,l’attributétantparfaitement visibledepuisl’extérieur.
1. Le hashage d’un mot de passe correspond à une opération non-réversible qui permet de calculer un condensat (hash) du mot de passe. Ce condensat peut-être utilisé pour vérifier la validité d’un mot de passe, mais ne permet pas de retrouver le mot de passe d’origine.
2. C++, Java, Ruby, etc.
Ilrestepossibledemasquerunpeuplusl’attributàl’aideduprééfixeapoureffet derenommerl’attributenyinsérantlenomdelaclassecourante.
1 classUser:
2 def__init__(self, id, name, password):
3 = id
4 = name
5 self.__salt = crypt.mksalt()
6 self.__password = self.__crypt_pwd(password)
7
8 def__crypt_pwd(self, password):
9 return crypt.crypt(password, self.__salt)
10
11 defcheck_pwd(self, password):
12 return self.__password == self.__crypt_pwd(password)
1 >>> john = User(1, 'john', '12345')
2 >>> john.__password
3 Traceback (most recent call last):
4 File "<stdin>", line 1, in <module>
5 AttributeError: 'User' object has no attribute '__password'
6 >>> john._User__password
7 '$6$kjwoqPPHRQAamRHT$591frrNfNNb3.RdLXYiB/bgdCC4Z0p.B'
Ce comportement pourra surtout être utile pour éviter des conflits de noms entre attributs internesdeplusieursclassessurunmêmeobjet,quenousverronslorsdel’héritage.
3.4. Tu aimes les glaces, canard ?
UnobjetenPythonestdéfiniparsastructure(lesattributsqu’ilcontientetlesméthodesqui luisontapplicables)plutôtqueparsontype.
Ainsi,pourfairesimple,unfichierseraunobjetpossédantdesméthodesread,writeetclose.
ToutobjetrespectantcettedéfinitionseraconsidéréparPythoncommeunfichier.
Pythonestentièrementconstruitautourdecetteidée,appelée duck-typing :« Sijevoisun animalquivolecommeuncanard,cancanecommeuncanard,etnagecommeuncanard,alors j’appellecetoiseauuncanard »(JamesWhitcombRiley)
3.5. TP : Forum, utilisateurs et messages
Pourcepremier TP,nousallons nousintéresseraux classesd’unforum.Fortsdenotretype Userpourreprésenterunutilisateur,noussouhaitonsajouteruneclassePost,correspondantà unquelconquemessage.
Cetteclasseserainititaliséeavecunauteur(unobjetUser)etuncontenutextuel(lecorpsdu message).Unedateseradeplusgénéréelorsdelacréation.
for | mat |
UnPostpossèderauneméthodepourretournerlemessageformaté,correspondantau HTMLsuivant :
Deplus,nousajouteronsuneméthodepostànotreclasseUser,recevantuncorpsdemessage enparamètreetretournantunnouvelobjetPost.
15 return self._password == self._crypt_pwd(password)
16
17 defpost(self, message):
18 return Post(self, message)
19
20 classPost:
21 def__init__(self, author, message):
22 self.author = author
23 self.message = message
24 = ()
25
26 defformat(self):
27 date = .strftime('le %d/%m/%Y à %H:%M:%S')
28 return
'<div><span>Par {} {}</span><p>{}</p></div>'.format( date, self.message) 29
30 if __name__ == '__main__':
31 user = User(1, 'john', '12345')
32 p = ('Salut à tous')
33 print(p.format())
Noussavonsmaintenantdéfiniruneclasseetsesméthodes,initialisernosobjets,etprotégerles nomsd’attributs/méthodes.
Maisjusqu’ici,quandnousvoulonsétendrelecomportementd’uneclasse,nouslaredéfinissons entièrementenajoutantdenouveauxattributs/méthodes.Lechapitresuivantprésentel’héritage, unconceptquipermetd’étendreuneouplusieursclassessanstoucheraucodeinitial.
4. Extension et héritage
Iln’estpasdanscechapitrequestionderéglerlasuccessiondevotregrand-tanteparalliance, maisdenousintéresseràl’extensiondeclasses.
Ad | min |
Ad | min |
Imaginons que nous voulions définir une classe, pour gérer des administrateurs, qui réutiliseraitlemêmecodequelaclasseUser.Toutcequenoussavonsfaireactuellementc’est copier/collerlecodedelaclasseUserenchangeantsonnompour.
Nousallonsmaintenantvoircommentfaireçademanièreplusélégante,grâceàl’hé étudieronsdepluslesrelationsentreclassesansicréées.
NousutiliseronsdonclaclasseUsersuivantepourlasuitedecechapitre.
1 classUser:
2 def__init__(self, id, name, password):
3 = id
4 = name
5 self._salt = crypt.mksalt()
6 self._password = self._crypt_pwd(password)
7
8 def_crypt_pwd(self, password):
9 return crypt.crypt(password, self._salt)
10
11 defcheck_pwd(self, password):
12 return self._password == self._crypt_pwd(password)
4.1. Hériter en toute simplicité
L’héritagesimpleestlemécanismepermettantd’étendreuneuniqueclasse.Ilconsisteàcréer unenouvelleclasse(fille)quibénéficieradesmêmesméthodesetattributsquesaclassemè seraaiséd’endéfinirdenouveauxdanslaclassefille,etcelan’altèrerapaslefonctionnementde lamère.
Ad | min | ma | nage |
Parexemple,nousvoudrionsétendrenotreclasseUserpourajouterlapossibilitéd’avoirdes administrateurs. Les administrateurs ( ) possèderaient une nouvelle méthode, , pouradministrerlesystème.
Ad | min |
ma | nage |
EnplusdesméthodesdelaclasseUser(__init__,_crypt_pwdetcheck_pwd),possède aussiuneméthode.
1 >>> root = Admin(1, 'root', 'toor')
2 >>> root.check_password('toor')
3 True
4 >>> root.manage()
5 I am an über administrator!
6 >>> john = User(2, 'john', '12345')
7 >>> john.manage()
8 Traceback (most recent call last):
9 File "<stdin>", line 1, in <module>
10 AttributeError: 'User' object has no attribute 'manage'
Nouspouvonsavoirdeuxclassesdifférenteshéritantd’unemêmemère
Ad | min |
etGuestsontalorsdeuxclassesfillesdeUser.
L’héritagesimplepermetaussid’hériterd’uneclassequihériteelle-mêmed’uneautreclasse.
Su | per | Ad | min | Ad | min |
Su | per | Ad | min |
estalorslafillede ,elle-mêmelafilledeUser.OnditalorsqueUserest uneancêtrede.
Onpeutconstaterquelssontlesparentsd’uneclasseàl’aidedel’attributspécial__bases__ desclasses :
QuevaudraitalorsUser.__bases__,sachantquelaclasseUserestdéfiniesanshéritage ?
ob | ject | ob | ject |
ob | ject |
Onremarqueque,sansquenousn’ayonsriendemandé,Userhéritede .Enfait, est l’ancêtre de toute classe Python. Ainsi, quand aucune classe parente n’est définie, c’est quiestchoisi.
4.1.1. Sous-typage
Nousavonsvuquel’héritagepermettaitd’étendrelecomportementd’uneclasse,maiscen’est pastout.L’héritageaaussidusensauniveaudestypes,encréantunnouveautypecompatible avecleparent.
isins | tance |
EnPython,lafonctionpermetdetestersiunobjetestl’instanced’unecertaine classe.
isins | tance |
Mais gardez toujours à l’esprit qu’en Python, on préfère se référer à la structure d’un objet qu’àsontype(duck-typing),lestestsàbasedesontdoncàutiliserpourdescas particuliersuniquement,oùilseraitdifficiledeprocéderautrement.
4.2. La redéfinition de méthodes, c’est super !
Nous savons hériter d’une classe pour y insérer de nouvelles méthodes, mais nous ne savons pasétendrelesméthodesdéjàprésentesdanslaclassemère.Laredéfinitionestunconceptqui permetderemplaceruneméthodeduparent.
NousvoudrionsquelaclasseGuestnepossèdeplusaucunmotdepasse.Celle-cidevramodifier laméthodecheck_pwdpouracceptertoutmotdepasse,etsimplifierlaméthode__init__.
On ne peut pas à proprement parler étendre le contenu d’une méthode, mais on peut la redéfinir :
Cela fonctionne comme souhaité, mais vient avec un petit problème, le code de la méthode
__init__ est répété. En l’occurrence il ne s’agit que de 2 lignes de code, mais lorsque nous voudronsapporterdesmodificationsàlaméthodedelaclasseUser,ilfaudralesrépercutersur Guest,cequidonnevitequelquechosededifficileàmaintenir.
su | per |
Heureusement,Pythonnousoffreunmoyenderemédieràcemécanisme,super !Oui,, littéralement, une fonction un peu spéciale en Python, qui nous permet d’utiliser la classe parente(superclass).
su | per |
estunefonctionquiprendinitialementenparamètreuneclasseetuneinstancedecette
classe.Elleretourneunobjet proxy3 quis’utilisecommeuneinstancedelaclasseparente.
su | per |
Auseindelaclasseenquestion,lesargumentsdepeuventêtreomis(ilscorrespondront àlaclasseetàl’instancecourantes),cequinouspermetdesimplifiernotreméthode__init__ etd’éviterlesrépétitions.
Onnoteratoutdemêmequecontrairementauxversionsprécédentes,l’initialisateurdeUser estappeléenplusdeceluideGuest,etdoncqu’unseletun hash dumotdepassesontgénérés alorsqu’ilsneservirontpas.
3. Un proxy est un intermédiaire transparent entre deux entités.
Çan’estpastrèsgravedanslecasprésent,maispensez-ydansvosdéveloppementsfuturs,afin denepasexécuterd’opérationscoûteusesinutilement.
4.3. Une classe avec deux mamans
Avecl’héritagesimple,nouspouvionsétendrelecomportementd’uneclasse.L’héritagemultiple vanouspermettredelefairepourplusieursclassesàlafois.
Ilnoussuffitdepréciserplusieursclassesentreparenthèseslorsdelacréationdenotreclasse fille.
NotreclasseCadoncdeuxmères :AetB.CelaveutaussidirequelesobjetsdetypeCpossèdent àlafoislesméthodesfooetbar.
4.3.1. Ordre d’héritage
L’ordredanslequelonhéritedesparentsestimportant,ildéterminedansquelordrelesméthodes serontrecherchéesdanslesclassesmères.Ainsi,danslecasoùlaméthodeexistedansplusieurs parents,celledelapremièreclasseseraconservée.
Cet ordre dans lequel les classes parentes sont explorées pour la recherche des méthodes est appelé Method Resolution Order (MRO).Onpeutleconnaîtreàl’aidedelaméthodemrodes classes.
1 >>> A.mro()
2 [<class '__main__.A'>, <class 'object'>]
3 >>> B.mro()
4 [<class '__main__.B'>, <class 'object'>]
5 >>> C.mro()
6 [<class
'__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'
7 >>> D.mro()
8 [<class
'__main__.D'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'
su | per | su | per |
C’estaussice MRO quiestutilisépar pourtrouveràquelleclassefaireappel. se charged’explorerle MRO delaclassedel’instancequiluiestdonnéeensecondparamètre,et deretournerun proxy surlaclassejusteàdroitedecelledonnéeenpremierparamètre.
su | per(C, | c) |
su | per(A, | c) | B | su | per(B, | c) |
Ainsi,aveccuneinstancedeC,retourneraunobjetsecomportantcommeune instancedeA, commeuneinstancede ,et commeuneinstancede
ob | ject |
.
8 >>> super(B, c).foo() # -> méthode introuvable 9 Traceback (most recent call last):
10 File "<stdin>", line 1, in <module>
11 AttributeError: 'super' object has no attribute 'foo'
Lesclassesparentesn’ontalorspasbesoindeseconnaîtrelesuneslesautrespourseréférencer.
1 classA:
2 def__init__(self):
3 print("Début initialisation d'un objet de type A")
4 super().__init__()
5 print("Fin initialisation d'un objet de type A")
6
7 classB:
8 def__init__(self):
9 print("Début initialisation d'un objet de type B")
10 super().__init__()
11 print("Fin initialisation d'un objet de type B")
12
13 classC(A, B):
14 def__init__(self):
15 print("Début initialisation d'un objet de type C")
16 super().__init__()
17 print("Fin initialisation d'un objet de type C")
18
19 classD(B, A):
20 def__init__(self):
21 print("Début initialisation d'un objet de type D")
22 super().__init__()
23 print("Fin initialisation d'un objet de type D")
1 >>> C()
2 Début initialisation d'un objet de type C
3 Début initialisation d'un objet de type A
4 Début initialisation d'un objet de type B
5 Fin initialisation d'un objet de type B
6 Fin initialisation d'un objet de type A
7 Fin initialisation d'un objet de type C
8 <__main__.C object at 0x7f0ccaa970b8>
9 >>> D()
10 Début initialisation d'un objet de type D
11 Début initialisation d'un objet de type B
12 Début initialisation d'un objet de type A
13 Fin initialisation d'un objet de type A
14 Fin initialisation d'un objet de type B
15 Fin initialisation d'un objet de type D
Laméthode__init__desclassesparentesn’estpasappeléeautomatiquement,etl’appeldoit doncêtreréaliséexplicitement.
su | per().__init__() |
C’estainsileprésentdanslaclasseCquiappellel’initialiseurdelaclasse
su | per().__init__() |
A,quiappellelui-mêmeceluidelaclasseB.Inversement,pourlaclasseD, appellel’initialiseurdeBquiappelleceluideA.
On notera que les exemple donnés n’utilisent jamais plus de deux classes mères, mais il est possibled’enavoirautantquevouslesouhaitez.
4.3.2. Mixins
Les mixins sontdesclassesdédiéesàunefonctionnalitéparticulière,utilisableenhéritantd’une classedebaseetdece mixin.
Parexemple,plusieurstypesquel’onconnaîtsontappelésséquences(str,list,tuple).Ils ontencommunlefaitd’implémenterl’opérateur[]etdegérerle slicing.Onpeutainsiobtenir l’objetenordreinverseàl’aidedeobj[::-1].
re | verse |
Un mixin quipourraitnousêtreutileseraituneclasseavecuneméthodepournous retournerl’objetinversé.
Ouencorenouspourrionsvouloirajouterlagestiond’unephotodeprofilànosclassesUseret dérivées.
4.4. TP : Fils de discussion
VousvoussouvenezdelaclassePostpourreprésenterunmessage ?Nousaimerionsmaintenant pouvoirinstancierdesfilsdediscussion(Thread)surnotreforum.
Qu’est-cequ’unfildediscussion ?
— Unmessageassociéàunauteuretàunedate ;
— Maisquicomporteaussiuntitre ;
— Etunelistedeposts(lesréponses).
LepremierpointindiqueclairementquenousallonsréutiliserlecodedelaclassePost,doncen hériter.
ans | wer |
for | mat |
Notre nouvelle classe sera initialisée avec un titre, un auteur et un message. Thread sera dotée d’une méthoderecevant un auteur et un texte, et s’occupant de créer le post correspondantetdel’ajouteraufil.NouschangeronsaussilaméthodeduThreadafin qu’elleconcatèneaufill’ensembledesesréponses.
LaclassePostresterainchangée.Enfin,noussupprimeronslaméthodepostdelaclasseUser, pourluienajouterdeuxnouvelles :
new_thread(title, | mes | sage) |
—pour créer un nouveau fil de discussion associé à cet utilisateur ;
ans | wer_thread(thread, | mes | sage) |
—pourrépondreàunfilexistant.
1 importcrypt
2 importdatetime 3
4 classUser:
5 def__init__(self, id, name, password):
6 = id
7 = name
8 self._salt = crypt.mksalt()
9 self._password = self._crypt_pwd(password)
10
11 def_crypt_pwd(self, password):
12 return crypt.crypt(password, self._salt)
13
14 defcheck_pwd(self, password):
15 return self._password == self._crypt_pwd(password)
16
17 defnew_thread(self, title, message):
18 return Thread(title, self, message)
19
20 defanswer_thread(self, thread, message):
21 thread.answer(self, message)
22
23 classPost:
24 def__init__(self, author, message):
25 self.author = author
26 self.message = message
27 = ()
28
29 defformat(self):
30 date = .strftime('le %d/%m/%Y à %H:%M:%S')
31 return
'<div><span>Par {} {}</span><p>{}</p></div>'.format( date, self.message)
32
33 classThread(Post):
34 def__init__(self, title, author, message):
35 super().__init__(author, message)
36 self.title = title
37 self.posts = []
38
39 defanswer(self, author, message):
40 self.posts.append(Post(author, message))
41
42 defformat(self):
43 posts = [super().format()]
44 posts += [p.format() for p in self.posts]
45 return '\n'.join(posts)
46
47 if __name__ == '__main__':
48 john = User(1, 'john', '12345')
49 peter = User(2, 'peter', 'toto')
50 thread = john.new_thread('Bienvenue', 'Bienvenue à tous')
51 peter.answer_thread(thread, 'Merci')
52 print(thread.format())
5. Opérateurs
add(a, | b) |
IlestmaintenanttempsdenousintéresserauxopérateursdulangagePython(+,-,*,etc.).En effet,uncoderespectantlaphilosophiedulangagesedoitdelesutiliseràbonescient. Ilssontunemanièreclairedereprésenterdesopérationsélémentaires(addition,concaténation, …)entredeuxobjets.a+besteneffetpluslisiblequ’(b).
Cechapitreapourbutdevousprésenterlesmécanismesmisenjeuparcesdifférentsopérateurs, etlamanièredelesimplémenter.
5.1. Des méthodes un peu spéciales
Nousavonsvuprécédemmentlaméthode__init__,permettantd’initialiserlesattributsd’un objet.Onappellecetteméthodeuneméthodespéciale,ilyenaencorebeaucoupd’autresen Python.Ellessontreconnaissablesparleurnomdébutantetfinissantpardeux underscores.
Vousvousêtespeut-êtredéjàdemandéd’oùprovenaitlerésultataffichésurlaconsolequandon entresimplementlenomd’unobjet.
Il s’agit en fait de la représentation d’un objet, calculée à partir de sa méthode spéciale __repr__.
Ànoterqu’uneméthodespécialen’estpresquejamaisdirectementappeléeenPython,onlui préféreradanslecasprésentlafonction builtin repr.
Il nous suffit alors de redéfinir cette méthode __repr__ pour bénéficier de notre propre représentation.
Uneautreopérationcouranteestlaconversiondenotreobjetenchaînedecaractèresafind’être affichééfaut,laconversionenchaînecorrespondàlareprésentation del’objet,maisellepeutêtresurchargéeparlaméthode__str__.
5.2. Doux opérateurs
Les opérateurs sont un autre type de méthodes spéciales que nous découvrirons dans cette section.
ope | ra | tor |
Eneffet,lesopérateursnesontriend’autresenPythonquedesfonctions,quis’appliquentsur leursopérandes.Onpeuts’enrendrecompteàl’aidedumodule,quirépertorieles fonctionsassociéesàchaqueopérateur.
Ainsi,chacundesopérateurscorrespondraàuneméthodedel’opérandedegauche,quirecevra enparamètrel’opérandededroite.
5.2.1. Opérateurs arithmétiques
L’addition,parexemple,estdéfinieparlaméthode__add__.
Assezsimple,n’est-ilpas ?Maisnousn’avonspastoutàfaitterminé.Silaméthodeestappelée surl’opérandedegauche,quesepasse-t-ilquandnotreobjetsetrouveàdroite ?
1 >>> 5 + A()
2 Traceback (most recent call last):
3 File "<stdin>", line 1, in <module>
4 TypeError: unsupported operand type(s) for +: 'int' and 'A'
Nousnesupportonspascetteopération.Eneffet,l’expressionfaitappelàlaméthodeint.__add__ quineconnaîtpaslesobjetsdetypeA.Heureusement,cecasaétéprévuetilexisteunefonction inverse,__radd__,appeléesilapremièreopérationn’étaitpassupportée.
IlfautbiennoterqueA.__radd__neseraappeléequesiint.__add__aéchoué.
Lesautresopérateursarithmétquesbinairesaurontuncomportementsimilaire,voiciuneliste desméthodesàimplémenterpourchacund’eux :
— Addition/Concaténation (a+b)—__add__,__radd__
— Soustraction/Différence (a-b)—__sub__,__rsub__
— Multiplication (a*b)—__mul__,__rmul__
__true | div__ | __rtrue | div__ |
— Division (a/b)— ,
__floor | div__ | __rfloor | div__ |
— Division entière (a//b)— ,
— Modulo/Formattage (a%b)—__mod__,__rmod__
— Exponentiation (a**b)—__pow__,__rpow__
Onremarqueaussiquechacundecesopérateursarithmétiquespossèdeuneversionsimplifiée pourl’assignation(a+=b)quicorrespondàlamééfaut,lesméthodes __add__/__radd__ sont appelées, mais définir __iadd__ permet d’avoir un comportement différentdanslecasd’unopérateurd’assignation,parexemplesurleslistes :
5.2.2. Opérateurs arithmétiques unaires
Voicipourlesopérateursbinaires,voyonsmaintenantlesopérateursunaires,quineprennent doncpasd’autreparamètrequeself.
— Opposé (-a)—__neg__
— Positif (+a)-__pos__
— Valeur abosule (abs(a))—__abs__
5.2.3. Opérateurs de comparaison
Delamêmemanièrequepourlesopérateursarithmétiques,nousavonsuneméthodespéciale paropérateurdecomparaison.Cesopérateurss’appliquerontsurl’opérandegaucheenrecevant ledroitenparamètre.Ilsdevrontretournerunbooléen.
Contrairementauxopérateursarithmétiques,iln’estpasnécessaired’avoirdeuxversionspour chaqueopérateurpuisquePythonsauradirectementquelleopérationinversetestersilapremière aéchoué(a==bestéquivalentàb==a,a<bàb>a,etc.).
— Égalité (a==b)—__eq__
— Différence (a!=b)—__neq__
— Stricte infériorité (a<b)—__lt__
— Infériorité (a<=b)—__le__
— Stricte supériorité (a>b)—__gt__
— Supériorité (a>=b)—__ge__
to | tal_or | de | ring | func | tools |
Onnoteraaussiquebeaucoupdecesopérateurspeuvents’inférerlesunslesautres.Parexemple, ilsuffitdesavoircalculera==beta<bpourdéfinirtouteslesautresopérations.Ainsi,Python dispose d’un décorateur, du module , pour automatiquement générerlesopérationsmanquantes.
5.2.4. Autres opérateurs
Nousavonsiciétudiélesprincipauxopérateursdulangage.Ceslistesnesontpasexhaustiveset présententjustelaméthodologieàsuivre.
ope | ra | tor |
Pour une liste complète, je vous invite à consulter la documentation du module : .
5.3. TP : Arithmétique simple
Oublions temporairement nos utilisateurs et notre forum, et intéressons-nous à l’évaluation mathématique.
Imaginonsquenousvoulionsreprésenteruneexpressionmathématique,quipourraitcontenir destermesvariables(parexemple,2*(-x+1)).
Ilvanousfalloirutiliseruntypepourreprésentercettevariablex,appeléVar,etunsecond pourl’expressionnonévaluée,Expr.LesVarétantuntypeparticulierd’expressions. Nousauronsdeuxautrestypesd’expressions :lesopérationsarithmétiquesunaires(+,-)et binaires(+,-,*,/,//,%,**).Vouspouvezvousappuyerunmêmetypepourcesdeuxtypes d’opérations.
L’expressionprécédentes’évalueraitparexempleà :
1 BinOp(, 2, BinOp(, UnOp(, Var('x')), 1))
com | pute(**va | lues) |
Var('x').com | pute(x=5) |
NousajouteronsànotretypeExpruneméthode,quipermettradecalculer l’expressionsuivantunevaleurdonnée,defaçonàcequeretourne
5.
Enfin,nouspourronsajouteruneméthode__repr__pourobtenirunereprésentationlisiblede notreexpression.
18 | def__add__(self, rhs): | ||
19 | return BinOp(, | self, rhs, '+') | |
20 21 | def__radd__(self, lhs): | ||
22 | return BinOp(, | lhs, self, '+') | |
23 24 | def__sub__(self, rhs): | ||
25 | return BinOp(, | self, rhs, '-') | |
26 27 | def__rsub__(self, lhs): | ||
28 | return BinOp(, | lhs, self, '-') | |
29 30 | def__mul__(self, rhs): | ||
31 | return BinOp(, | self, rhs, '*') | |
32 33 | def__rmul__(self, lhs): | ||
34 | return BinOp(, | lhs, self, '*') | |
35 36 | def__truediv__(self, rhs): | ||
37 | return BinOp(operator.truediv, self, rhs, '/') | ||
38 39 | def__rtruediv__(self, lhs): | ||
40 | return BinOp(operator.truediv, lhs, self, '/') | ||
41 42 | def__floordiv__(self, rhs): | ||
43 | '//') | ||
44 45 | def__rfloordiv__(self, lhs): | ||
46 | return BinOp(operator.floordiv, lhs, self, | '//') | |
47 48 | def__mod__(self, rhs): | ||
49 | return BinOp(, self, rhs, '*') | ||
50 51 | def__rmod__(self, lhs): | ||
52 | return BinOp(, lhs, self, '*') | ||
53 54 | classVar(Expr): | ||
55 | def__init__(self, name): | ||
56 | = name | ||
57 58 | defcompute(self, **values): | ||
59 | ifin values: | ||
60 | return values[] | ||
61 | return self | ||
62 63 | def__repr__(self): | ||
64 | return | ||
65 66 | classOp(Expr): | ||
67 | def__init__(self, op, *args): | ||
Les opérateurs sont une notion importante en Python, mais ils sont loin d’être la seule. Le chapitresuivantvousprésenterad’autresconceptsavancésduPython,qu’ilestimportantde connaître,pourêtreenmesuredelesutiliserquandcelas’avèrenécessaire.
6. Programmation orientée objet avancée
Danscechapitre,nousallonsnousintéresseràdesconceptsplusavancésdeprogrammation orientéeobjetdisponiblesenPython,telsquelesattributs/méthodesdeclasseoulespropriétés.
6.1. Les attributs entrent en classe
Nousavonsdéjàrencontréunattributdeclasse,quandnousnousintéressionsauxparentsd’une classe.Souvenez-vousde__bases__,nousnel’utilisionspassurdesinstancesmaissurnotre classedirectement.
EnPython,lesclassessontdesobjetscommelesautres,etpeuventdoncposséderleurspropres attributs.
Lesattributsdeclassepeuventaussisedéfinirdanslecorpsdelaclasse,delamêmemanière quelesméthodes.
Onnoteraàl’inversequ’ilestaussipossiblededéfiniruneméthodedelaclassedepuisl’extérieur :
L’avantagedesattributsdeclasse,c’estqu’ilssontaussidisponiblespourlesinstancesdecette classe.Ilssontpartagéspartouteslesinstances.
C’estlefonctionnementdu MRO dePython,ilcherched’abordsil’attributexistedansl’objet, puissicen’estpaslecas,lecherchedanslesclassesparentes.
Attentiondonc,quandl’attributestredéfinidansl’objet,ilseratrouvéenpremier,etn’affectera paslaclasse.
Attention aussi, quand l’attribut de classe est un objet mutable4, il peut être modifié par n’importequelleinstancedelaclasse.
L’attributdeclasseestaussiconservélorsdel’héritage,etpartagéaveclesclassesfilles(sauf lorsquelesclassesfillesredéfinissentl’attribut,delamêmemanièrequepourlesinstances).
4. Un objet mutable est un objet que l’on peut modifier (liste, dictionnaire) par opposition à un objet immutable (nombre, chaîne de caractères, tuple).
6.2. La méthode pour avoir la classe
Commepourlesattributs,desméthodespeuventêtredéfiniesauniveaudelaclasse.C’estpar exemplelecasdelaméthodemro.
Lesméthodesdeclasseconstituentdesopérationsrelativesàlaclassemaisàaucuneinstance. Ellesrecevrontlaclassecouranteenpremierparamètre(nommécls,correspondantauself desméthodesd’instance),etaurontdoncaccèsauxautresattributsetméthodesdeclasse.
ReprenonsnotreclasseUser,àlaquellenousvoudrionsajouterlestockagedetouslesutilisateurs, et la génération automatique de l’id. Il nous suffirait d’une même méthode de classe pour stockerl’utilisateurdansunattributdeclasseusers,etquiluiattribueraitunidenfonction dunombred’utilisateursdéjàenregistrés.
class | me | thod |
Lesméthodesdeclassesedéfinissentcommelesméthodeshabituelles,àladifférenceprèsqu’elles sontprécédéesdudécorateur.
Vouspouvezconstaterlerésultatenréessayantlecodedonnéplushaut.
6.3. Le statique c’est fantastique
Lesméthodesstatiquessonttrèsprochesdesméthodesdeclasse,maissontplusàconsidérer commedesfonctionsauseind’uneclasse.
Contrairement aux méthodes de classe, elles ne recevront pas le paramètre cls, et n’auront doncpasaccèsauxattributsdeclasse,méthodesdeclasseouméthodesstatiques.
Lesméthodesstatiquessontplutôtdédiéesàdescomportementsannexesenrapportavecla classe, par exemple on pourrait remplacer notre attribut id par un uuid aléatoire, dont la générationnedépendraitderiend’autredanslaclasse.
sta | tic | me | thod |
Ellessedéfinissentavecledécorateur.
6.4. Attribut es-tu là ?
Nous savons récupérer et assigner un attribut dont le nom est fixé, cela se fait facilement à l’=value.
Maisnousest-ilpossibled’accéderàdesattributsdontlenomestvariable ?
PrenonsuneinstancejohndenotreclasseUser,etlenomd’unattributquenousvoudrions extraire :
ge | tattr |
Lafonctionnouspermetalorsderécupérercetattribut.
ge | tattr(obj, | 'foo') |
Ainsi,estéquivalentà.
ha | sattr |
ge | tattr |
Ontrouveaussiunefonctionpourtesterlaprésenced’ estconstruitecommemaisretourneunbooléenpoursavoirsil’attributestprésentou non. supprimerunattribut.
1 >>> setattr(john, 'name', 'peter') # équivalent à ` = 'peter'`
2 >>>
3 'peter'
4 >>> delattr(john, 'name') # équivalent à `del `
5 >>>
6 Traceback (most recent call last):
7 File "<stdin>", line 1, in <module>
8 AttributeError: 'User' object has no attribute 'name'
6.5. La dynamique des propriétés
Les propriétés sont une manière en Python de « dynamiser » les attributs d’un objet. Ils permettentdegénérerdesattributsàlavoléeàpartirdeméthodesdel’objet.
Unexemplevalantmieuxqu’unlongdiscours :
pic | ture |
pic | ture |
On définit donc une propriété, qui s’utilise comme un attribut. Chaque fois qu’on appelle,laméthodecorrespondanteestappeléeetlerésultatestcalculé.
pic | ture |
Ils’agitlàd’unepropriétéenlectureseule,ilnousesteneffetimpossibledemodifierlavaleur del’attribut.
1 >>> john.picture = ''
2 Traceback (most recent call last):
3 File "<stdin>", line 1, in <module>
4 AttributeError: can't set attribute
@pic | ter | set | ter |
Pour le rendre modifiable, il faut ajouter à notre classe la méthode permettant de gérer la modification,àl’aidedudécorateur (ledécorateur denotrepropriété
pic | ture |
,donc).
_pic | ture |
On utilisera ici un attribut, qui pourra contenir l’adresse de l’image si elle a été définie,etNonelecaséchéant.
@pic | le | ter |
_pic | ture |
Enfin,onpeutaussicoderlasuppressiondel’attributàl’aidede,cequi revientàréaffecterNoneàl’attribut.
6.6. L’art des classes abstraites
La notion de classes abstraites est utilisée lors de l’héritage pour forcer les classes filles à implémentercertainesméthodes(ditesméthodesabstraites)etdoncrespecteruneinterface.
abs | tract | me | thod |
Les classes abstraites ne font pas partie du cœur même de Python, mais sont disponibles viaunmoduledelabibliothèquestandard,abc(Abstract Base Classes).Cemodulecontient notammentlaclasseABCetledécorateur,pourdéfinirrespectivementune classeabstraiteetuneméthodeabstraitedecetteclasse.
Une classe abstraite doit donc hériter d’ABC, et utiliser le décorateur cité pour définir ses méthodesabstraites.
Ilnousestimpossibled’instancierdesobjetsdetypeMyABC,puisqu’uneméthodeabstraiten’est pasimplémentée :
1 >>> MyABC()
2 Traceback (most recent call last): 3 File "<stdin>", line 1, in <module> 4 TypeError:
Can't instantiate abstract class MyABC with abstract methods foo
IlenestdemêmepouruneclassehéritantdeMyABCsansredéfinirlaméthode.
Aucunproblèmeparcontreavecuneautreclassequiredéfinitbienlaméthode.
6.7. TP : Base de données
PourcedernierTP,nousaborderonslesméthodesdeclasseetlespropriétés.
Reprenonsnotreforum,auquelnoussouhaiterionsajouterlagestiond’unebasededonnées.
in | sert | se | lect |
Notrebasededonnéesserauneclasseavecdeuxméthodes, et .Sonimplémentationestlibre,elledoitjusterespecterl’interfacesuivante :
Mo | del |
Mo | del |
se | lect |
Mo | del |
Nousajouteronsensuiteuneclasse,quisechargeradestockerdanslabasetoutesles instancescréées.comprendrauneméthodedeclasseget(**kwargs)chargéederéaliser unerequêtesurlabasededonnéesetderetournerl’objetcorrespondant.Lesobjetsde typedisposerontaussid’unepropriétéid,retournantunidentifiantuniquedel’objet.
Mo | del |
Mo | del |
On pourra alors faire hériter nos classes User et Post de, afin que les utilisateurs et messagessoientstockésenbasededonnées.Dansunsecondtemps,onpourrafairede uneclasseabstraite,parexempleenrendantabstraitelaméthode__init__.
41 self.message = message
42 = ()
43
44 defformat(self):
45 date = .strftime('le %d/%m/%Y à %H:%M:%S')
46 return
'<div><span>Par {} {}</span><p>{}</p></div>'.format( date, self.message) 47
48 if __name__ == '__main__':
49 john = User('john')
50 peter = User('peter')
51 Post(john, 'salut')
52 Post(peter, 'coucou')
53
54 print(((name='peter')).format())
55 print((()).format())
CesdernièresnotionsontdûcomplétervosconnaissancesdumodèleobjetdePython,etvous devriezmaintenantêtreprêtsàvouslancerdansunprojetexploitantcesconcepts.
7. Conclusion
Ce cours touche maintenant à sa fin, mais votre apprentissage du Python continue. Avec la programmationobjet,unnouveaumondes’offreàvous.
Vousallez pouvoirprendre en mains des frameworks tels que Django si le développement
Webvousintéresse.OuencoredesbibliothèquescommePyGTK sivousêtesplutôtattirés parlaprogrammationde GUI.
Enfin,sivousvoulezcomplétervotrecompréhensiondumodèleobjetdePython,jepeuxvous orienterverscecourssurlesnotionsavancéesdulangage .