{"id":138,"date":"2026-09-28T20:41:12","date_gmt":"2026-09-28T18:41:12","guid":{"rendered":"https:\/\/www.ranksignal.fr\/blog\/?p=138"},"modified":"2026-10-04T17:58:19","modified_gmt":"2026-10-04T15:58:19","slug":"de-la-fonction-a-la-classe-python","status":"publish","type":"post","link":"https:\/\/www.ranksignal.fr\/blog\/python\/de-la-fonction-a-la-classe-python\/","title":{"rendered":"De la fonction \u00e0 la classe en Python : le grand saut, pas \u00e0 pas"},"content":{"rendered":"<p>Ton script tourne. Il est fait de fonctions, chacune fait son travail, et pourtant chaque nouvelle fonctionnalit\u00e9 oblige \u00e0 tra\u00eener une variable de plus d&rsquo;un appel \u00e0 l&rsquo;autre. Un jour, tu ouvres le code d&rsquo;un coll\u00e8gue et tu y trouves <code>self<\/code>, <code>__init__<\/code>, des classes qui h\u00e9ritent d&rsquo;autres classes.<\/p>\n<p>Bonne nouvelle : ce saut n&rsquo;est pas un changement de langage, c&rsquo;est un changement de rangement. Voici comment le franchir sans te perdre.<\/p>\n<h2>L&rsquo;essentiel en 30 secondes<\/h2>\n<ul>\n<li>Une <strong>classe<\/strong> regroupe au m\u00eame endroit des donn\u00e9es (ses attributs) et les fonctions qui travaillent dessus (ses m\u00e9thodes).<\/li>\n<li>Le constructeur <code>__init__()<\/code> pr\u00e9pare chaque objet ; les variables d&rsquo;instance sont propres \u00e0 chaque objet, les variables de classe sont partag\u00e9es.<\/li>\n<li>Les <strong>m\u00e9thodes magiques<\/strong> (<code>__str__()<\/code>, <code>__lt__()<\/code>, <code>__iter__()<\/code>\u2026) apprennent \u00e0 tes objets \u00e0 r\u00e9agir aux op\u00e9rateurs et aux boucles de Python.<\/li>\n<li>L&rsquo;<strong>h\u00e9ritage<\/strong> permet de r\u00e9utiliser une classe existante ; le <strong>polymorphisme<\/strong> permet \u00e0 une m\u00eame fonction de traiter des objets de classes diff\u00e9rentes.<\/li>\n<li>En Python, rien n&rsquo;est vraiment priv\u00e9 : c&rsquo;est une affaire de convention.<\/li>\n<\/ul>\n<h2>L&rsquo;intuition : ranger ce qui va ensemble<\/h2>\n<p>Imagine un petit jeu \u00e0 deux joueurs. Il te faut deux scores, une op\u00e9ration pour ajouter des points, une pour remettre les compteurs \u00e0 z\u00e9ro, une pour afficher le score et une pour d\u00e9signer le vainqueur. Avec de simples fonctions, il faut passer les scores de l&rsquo;une \u00e0 l&rsquo;autre \u00e0 chaque appel, et c&rsquo;est \u00e0 toi de ne jamais les m\u00e9langer.<\/p>\n<p>L&rsquo;approche orient\u00e9e objet propose autre chose : fabriquer <strong>un seul objet<\/strong> qui poss\u00e8de les deux scores <em>et<\/em> sait faire ces quatre op\u00e9rations. Tu ne manipules plus des variables \u00e9parpill\u00e9es, mais un \u00ab suivi de score \u00bb auquel tu donnes des ordres. C&rsquo;est exactement ce que fait la classe <code>ScoreTracker<\/code> un peu plus bas.<\/p>\n<blockquote>\n<p><strong>\ud83d\udca1 Intuition<\/strong> Une classe, c&rsquo;est un plan de fabrication ; chaque objet cr\u00e9\u00e9 \u00e0 partir de ce plan emporte ses propres donn\u00e9es avec lui, et conna\u00eet les op\u00e9rations qui s&rsquo;y appliquent.<\/p>\n<\/blockquote>\n<h2>Sous le capot : les briques d&rsquo;une classe<\/h2>\n<h3>Une classe, un constructeur, un attribut<\/h3>\n<p>Voici la classe la plus simple qui soit : elle a un constructeur <code>__init__()<\/code> qui pose un attribut <code>x<\/code>, et une m\u00e9thode qui l&rsquo;affiche.<\/p>\n<pre><code>#!\/usr\/bin\/env python\n\nclass MyClass:\n    def __init__(self):\n          self.x = 5\n\n    def main(self):\n          print('inside main')\n          print('x:',self.x)\n\nif __name__ == '__main__':\n    myClass = MyClass()\n    myClass.main()\n<\/code><\/pre>\n<p>Le bloc <code>if __name__ == '__main__':<\/code> est un idiome que tu croiseras partout : il n&rsquo;ex\u00e9cute son contenu que si le fichier est lanc\u00e9 directement, jamais lorsqu&rsquo;il est simplement import\u00e9 par un autre programme. Trois fa\u00e7ons d&rsquo;\u00e9crire l&rsquo;en-t\u00eate existent (<code>class MyClass:<\/code>, <code>class MyClass():<\/code> ou <code>class MyClass(object):<\/code>), mais elles sont \u00e9quivalentes : en Python 3, toute classe h\u00e9rite implicitement de <code>object<\/code>.<\/p>\n<h3>Ce qui appartient \u00e0 l&rsquo;objet, ce qui appartient \u00e0 la classe<\/h3>\n<p>Une distinction change tout : une <strong>variable d&rsquo;instance<\/strong> est propre \u00e0 chaque objet, une <strong>variable de classe<\/strong> est partag\u00e9e par toutes les instances.<\/p>\n<pre><code>class Company:\n    # a shared class variable:\n    location = &quot;USA&quot;\n    def __init__(self, name):\n        # an instance variable:\n        self.name = name\n<\/code><\/pre>\n<p>Ici, <code>location<\/code> est identique pour toutes les entreprises cr\u00e9\u00e9es ; <code>name<\/code>, lui, est d\u00e9fini \u00e0 la cr\u00e9ation et diff\u00e8re d&rsquo;un objet \u00e0 l&rsquo;autre.<\/p>\n<h3>Public, prot\u00e9g\u00e9, priv\u00e9 : une question de politesse<\/h3>\n<p>En Python 3, tout est public par d\u00e9faut. Un tiret bas en pr\u00e9fixe (<code>_variable<\/code>) signale une variable prot\u00e9g\u00e9e, r\u00e9serv\u00e9e \u00e0 la classe et \u00e0 ses sous-classes ; un double tiret bas (<code>__variable<\/code>) signale une variable priv\u00e9e, r\u00e9serv\u00e9e \u00e0 la classe elle-m\u00eame. Mais rien n&#8217;emp\u00eache techniquement d&rsquo;y acc\u00e9der de l&rsquo;ext\u00e9rieur, contrairement \u00e0 Java o\u00f9 le compilateur l&rsquo;interdit.<\/p>\n<blockquote>\n<p><strong>\u26a0\ufe0f Pi\u00e8ge<\/strong> Un attribut \u00ab priv\u00e9 \u00bb en Python n&rsquo;est priv\u00e9 que par convention : c&rsquo;est ton engagement d&rsquo;\u00e9quipe qui le prot\u00e8ge, pas le langage.<\/p>\n<\/blockquote>\n<h2>Le voir tourner : ton premier objet utile<\/h2>\n<p>Reprenons notre jeu \u00e0 deux joueurs. Observe comment les deux scores et les quatre op\u00e9rations vivent dans la m\u00eame classe, et comment le code appelant se contente de donner des ordres \u00e0 un objet.<\/p>\n<pre><code>class ScoreTracker:\n    def __init__(self, player1_initial=0,\nplayer2_initial=0):\n        self.player1 = player1_initial\n        self.player2 = player2_initial\n\n    def update_score(self, player_number,\nnew_points=None):\n        if player_number == 1:\n            self.player1 += (new_points or 1)\n        elif player_number == 2:\n            self.player2 += (new_points or 1)\n        else:\n            raise ValueError(&quot;Player number must be 1 or 2&quot;)\n\n    def reset_scores(self):\n        self.player1 = 0\n        self.player2 = 0\n\n    def display_scores(self, format_style=&quot;{} - {}&quot;):\n        return format_style.format(self.player1, self.player2)\n\n    def get_winner(self):\n        if self.player1 &gt; self.player2:\n            return &quot;Player 1&quot;\n        elif self.player2 &gt; self.player1:\n            return &quot;Player 2&quot;\n        else:\n            return &quot;Tie&quot;\n\n# Example usage\nscore_game = ScoreTracker()\nscore_game.update_score(1)\nscore_game.update_score(2, new_points=5) # Player 2 scores additional points\n\nprint(score_game.display_scores())\nprint(&quot;Current score:&quot;, score_game.player1, &quot;-&quot;,\nscore_game.player2)\n\nif score_game.get_winner():\n    print(f&quot;Winner is {score_game.get_winner()}!&quot;)\n\nscore_game.reset_scores()\nprint(&quot;Reset scores:&quot;, score_game.display_scores())\n<\/code><\/pre>\n<p>Trois choses \u00e0 retenir de cet exemple. Les scores sont cr\u00e9\u00e9s une fois, dans le constructeur, avec 0 par d\u00e9faut. Chaque m\u00e9thode travaille sur ces m\u00eames scores sans qu&rsquo;on ait \u00e0 les lui repasser. Enfin, l&rsquo;appel <code>update_score(2, new_points=5)<\/code> montre qu&rsquo;une m\u00e9thode se r\u00e8gle avec des arguments, exactement comme une fonction : ta culture des fonctions reste donc enti\u00e8rement valable.<\/p>\n<h3>Cacher le \u00ab comment \u00bb derri\u00e8re un \u00ab quoi \u00bb<\/h3>\n<p>Une classe peut aussi masquer sa cuisine interne. Ici, le code appelant ne conna\u00eet que <code>search_list()<\/code> ; le choix entre une recherche lin\u00e9aire et une recherche binaire se fait \u00e0 l&rsquo;int\u00e9rieur, dans des m\u00e9thodes pr\u00e9fix\u00e9es par <code>__<\/code>.<\/p>\n<pre><code>class MyEncapsulation:\n   def __init__(self,my_list):\n     self.my_list = my_list\n     self.sorted = False\n\n   # invokes a private method:\n   def search_list(self,item):\n     self.__search_my_list(item)\n\n   def __search_my_list(self,item):\n     if(self.sorted == False):\n        # perform a linear search:\n        self.__linear_search(item)\n     else:\n        # perform a binary search:\n        self.__binary_search(item)\n\n   def __linear_search(self,item):\n     # perform a linear search\n     pass\n\n   def __binary_search(self,item):\n     # perform a binary search\n     pass\n\nif __name__ == '__main__':\n    enc1 = MyEncapsulation([4,1,5,3,8])\n    enc1.search_list(5)\n<\/code><\/pre>\n<p>Le b\u00e9n\u00e9fice est concret : demain, on peut remplacer la recherche lin\u00e9aire par une binaire, ou l&rsquo;inverse, sans changer la signature publique <code>search_list()<\/code> ni le code qui l&rsquo;appelle. On appelle cela l&rsquo;<strong>encapsulation<\/strong> : faire \u00e9voluer l&rsquo;int\u00e9rieur d&rsquo;une classe sans casser ce qui en d\u00e9pend.<\/p>\n<h2>Faire parler tes objets avec les m\u00e9thodes magiques<\/h2>\n<p>Les m\u00e9thodes dont le nom commence et finit par deux tirets bas (<code>__add__()<\/code>, <code>__len__()<\/code>, <code>__str__()<\/code>\u2026) s&rsquo;appellent des <strong>m\u00e9thodes magiques<\/strong>, ou <em>dunders<\/em>. Elles permettent \u00e0 une classe personnalis\u00e9e de r\u00e9pondre aux op\u00e9rateurs standards de Python.<\/p>\n<p>Deux d&rsquo;entre elles d\u00e9cident de l&rsquo;affichage : <code>__str__()<\/code> vise un texte lisible pour l&rsquo;utilisateur final, <code>__repr__()<\/code> un texte utile au d\u00e9veloppeur. Si l&rsquo;une manque, Python retombe sur l&rsquo;autre, et <code>print()<\/code> appelle <code>__str__()<\/code> par d\u00e9faut.<\/p>\n<p>Autre exemple : pour dire ce que signifie \u00ab inf\u00e9rieur \u00e0 \u00bb entre deux points en 3D, on \u00e9crit <code>__lt__()<\/code>. On peut ensuite comparer deux objets avec l&rsquo;op\u00e9rateur <code>&lt;<\/code>, comme n&rsquo;importe quel type natif.<\/p>\n<pre><code>def __lt__(self, other):\n    if self.x &lt; other.x and self.y &lt; other.y and self.z &lt; other.z:\n        return True\n    else:\n        return False\n\nif(p1 &lt; p2): print(&quot;p1 &lt; p2&quot;)\nelif (p2 &lt; p1): print(&quot;p2 &lt; p1&quot;)\nelse: print(&quot;cannot be compared&quot;)\n<\/code><\/pre>\n<p>Ce m\u00eame <code>__lt__()<\/code> est ce qu&rsquo;utilise <code>queue.PriorityQueue<\/code> pour savoir quelle t\u00e2che traiter en premier : elle compare les t\u00e2ches entre elles, et les traite par priorit\u00e9 croissante, quel que soit leur ordre d&rsquo;insertion. Tes classes deviennent ainsi compatibles avec les outils de la biblioth\u00e8que standard.<\/p>\n<blockquote>\n<p><strong>\ud83d\udca1 Intuition<\/strong> Une m\u00e9thode magique, c&rsquo;est un contrat : \u00ab si tu me fournis cette m\u00e9thode, Python saura s&rsquo;en servir dans ses op\u00e9rateurs et ses boucles \u00bb.<\/p>\n<\/blockquote>\n<h3>Une classe qui se laisse parcourir<\/h3>\n<p>Le m\u00eame principe s&rsquo;applique aux boucles <code>for<\/code>. Un objet est <strong>it\u00e9rable<\/strong> d\u00e8s qu&rsquo;il d\u00e9finit <code>__iter__()<\/code>, qui doit renvoyer un it\u00e9rateur, c&rsquo;est-\u00e0-dire un objet dot\u00e9 de <code>__next__()<\/code>, charg\u00e9 de produire l&rsquo;\u00e9l\u00e9ment suivant ou de lever <code>StopIteration<\/code> quand tout est \u00e9puis\u00e9.<\/p>\n\n\n<figure data-wp-context=\"{&quot;imageId&quot;:&quot;6adef47170dc3&quot;}\" data-wp-interactive=\"core\/image\" data-wp-key=\"6adef47170dc3\" class=\"wp-block-image size-large wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"2400\" height=\"1350\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on--click=\"actions.showLightbox\" data-wp-on--load=\"callbacks.setButtonStyles\" data-wp-on--pointerdown=\"actions.preloadImage\" data-wp-on--pointerenter=\"actions.preloadImageWithDelay\" data-wp-on--pointerleave=\"actions.cancelPreload\" data-wp-on-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/09\/b1_ch02_protocole_iterateur.png\" alt=\"Le protocole d'it\u00e9ration : un objet it\u00e9rable fournit un it\u00e9rateur via iter(), puis la boucle for appelle __next__() jusqu'\u00e0 StopIteration\" class=\"wp-image-135\" srcset=\"https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/09\/b1_ch02_protocole_iterateur.png 2400w, https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/09\/b1_ch02_protocole_iterateur-300x169.png 300w, https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/09\/b1_ch02_protocole_iterateur-1024x576.png 1024w, https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/09\/b1_ch02_protocole_iterateur-768x432.png 768w, https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/09\/b1_ch02_protocole_iterateur-1536x864.png 1536w, https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/09\/b1_ch02_protocole_iterateur-2048x1152.png 2048w\" sizes=\"auto, (max-width: 2400px) 100vw, 2400px\" \/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\tdata-wp-bind--aria-label=\"state.thisImage.triggerButtonAriaLabel\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"state.thisImage.buttonRight\"\n\t\t\tdata-wp-style--top=\"state.thisImage.buttonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><figcaption class=\"wp-element-caption\">Sur le sch\u00e9ma, suis les fl\u00e8ches de gauche \u00e0 droite : <code>iter(obj)<\/code> d\u00e9clenche <code>__iter__()<\/code>, qui renvoie un it\u00e9rateur, puis la boucle <code>for<\/code> r\u00e9clame chaque \u00e9l\u00e9ment avec <code>__next__()<\/code> jusqu&rsquo;\u00e0 l&rsquo;exception finale.<\/figcaption><\/figure>\n\n\n<h2>R\u00e9utiliser sans copier : h\u00e9ritage et polymorphisme<\/h2>\n<h3>L&rsquo;h\u00e9ritage : \u00ab fais comme ma classe parente \u00bb<\/h3>\n<p>Quand une classe <code>B<\/code> h\u00e9rite d&rsquo;une classe <code>A<\/code>, tout ce que <code>A<\/code> d\u00e9finit devient accessible depuis <code>B<\/code>. Dans l&rsquo;exemple ci-dessous, la m\u00e9thode <code>func()<\/code> de <code>MyClassB<\/code> est mise en commentaire : c&rsquo;est donc celle de <code>MyClassA<\/code> qui s&rsquo;ex\u00e9cute quand on appelle <code>instanceB.func()<\/code>.<\/p>\n<pre><code>class MyClassA:\n   def __init__(self):\n      print('Hello from A')\n\n   def func(self):\n      print('Hello again from A')\n\nclass MyClassB(MyClassA):\n   def __init__(self):\n      print('Hello from B')\n\n  #def func(self):\n  # print('Hello again from B')\n\nif __name__ == '__main__':\n    instanceA = MyClassA()\n    instanceB = MyClassB()\n\n    print()\n    print('instanceA:')\n    instanceA.func()\n    print('instanceB:')\n    instanceB.func()\n<\/code><\/pre>\n\n\n<figure data-wp-context=\"{&quot;imageId&quot;:&quot;6adef47171332&quot;}\" data-wp-interactive=\"core\/image\" data-wp-key=\"6adef47171332\" class=\"wp-block-image size-large wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"1950\" height=\"1500\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on--click=\"actions.showLightbox\" data-wp-on--load=\"callbacks.setButtonStyles\" data-wp-on--pointerdown=\"actions.preloadImage\" data-wp-on--pointerenter=\"actions.preloadImageWithDelay\" data-wp-on--pointerleave=\"actions.cancelPreload\" data-wp-on-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/09\/b1_ch04_heritage_simple.png\" alt=\"H\u00e9ritage simple : MyClassB h\u00e9rite de MyClassA et, faute de red\u00e9finir func(), utilise celle de sa classe parente\" class=\"wp-image-136\" srcset=\"https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/09\/b1_ch04_heritage_simple.png 1950w, https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/09\/b1_ch04_heritage_simple-300x231.png 300w, https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/09\/b1_ch04_heritage_simple-1024x788.png 1024w, https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/09\/b1_ch04_heritage_simple-768x591.png 768w, https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/09\/b1_ch04_heritage_simple-1536x1182.png 1536w\" sizes=\"auto, (max-width: 1950px) 100vw, 1950px\" \/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\tdata-wp-bind--aria-label=\"state.thisImage.triggerButtonAriaLabel\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"state.thisImage.buttonRight\"\n\t\t\tdata-wp-style--top=\"state.thisImage.buttonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><figcaption class=\"wp-element-caption\">Regarde la fl\u00e8che \u00ab h\u00e9ritage \u00bb du sch\u00e9ma : tant que <code>MyClassB<\/code> ne red\u00e9finit pas <code>func()<\/code>, Python remonte vers la classe parente pour la trouver.<\/figcaption><\/figure>\n\n\n<h3>Un contrat pour les sous-classes<\/h3>\n<p>Une classe de base peut aussi servir de simple <strong>contrat<\/strong>. Dans un exemple de pr\u00e9f\u00e9rences alimentaires, la classe parente <code>MyFoodPrefs<\/code> l\u00e8ve volontairement <code>NotImplementedError<\/code> dans chacune de ses m\u00e9thodes : elle impose aux sous-classes <code>MyVegetarian<\/code> et <code>MyCarnivore<\/code> d&rsquo;\u00e9crire leur propre version. Chacune appelle <code>super().__init__()<\/code> pour d\u00e9clencher le constructeur de la classe parente. La version v\u00e9g\u00e9tarienne refuse d&rsquo;ajouter \u00ab Steak \u00bb ou \u00ab Chicken \u00bb, la version carnivore n&rsquo;applique jamais ce filtre.<\/p>\n<h3>Le polymorphisme : une fonction, plusieurs classes<\/h3>\n<p>C&rsquo;est ici que tes fonctions retrouvent leur place. Le <strong>polymorphisme<\/strong> d\u00e9signe la capacit\u00e9 d&rsquo;une m\u00eame fonction \u00e0 op\u00e9rer sur des objets de classes diff\u00e9rentes, pourvu qu&rsquo;ils exposent l&rsquo;interface attendue. Regarde <code>calcPerimeter()<\/code> : elle ne teste jamais le type de son argument.<\/p>\n<pre><code>class MyRect:\n   def perimeter(self):\n     print('Perimeter of a rectangle')\n\nclass MySquare:\n   def perimeter(self):\n     print('Perimeter of a square')\n\ndef calcPerimeter(obj):\n     obj.perimeter()\n\nif __name__ == '__main__':\n    instanceR = MyRect()\n    instanceS = MySquare()\n\n    print('instanceR:')\n    calcPerimeter(instanceR)\n    print('instanceS:')\n    calcPerimeter(instanceS)\n<\/code><\/pre>\n<p><code>MyRect<\/code> et <code>MySquare<\/code> n&rsquo;ont aucun lien de parent\u00e9 : seul compte le fait que les deux exposent une m\u00e9thode <code>perimeter()<\/code>. C&rsquo;est le <strong>duck typing<\/strong> : la fonction ne se soucie pas de la classe exacte, seulement de savoir si l&rsquo;objet \u00ab se comporte comme un canard \u00bb. Java exigerait une interface commune ; en Python, cette exigence reste implicite. Pour formaliser malgr\u00e9 tout le contrat, le module <code>abc<\/code> (Abstract Base Class) permet de d\u00e9finir des classes abstraites qui imposent les m\u00e9thodes \u00e0 impl\u00e9menter.<\/p>\n<blockquote>\n<p><strong>\ud83d\udccc \u00c0 retenir<\/strong> Passer aux objets ne t&rsquo;oblige pas \u00e0 jeter tes fonctions : une fonction qui accepte \u00ab n&rsquo;importe quel objet sachant faire ceci \u00bb est l&rsquo;une des formes les plus \u00e9l\u00e9gantes du polymorphisme.<\/p>\n<\/blockquote>\n<h2>Pi\u00e8ges et limites<\/h2>\n<ul>\n<li>\n<p><strong>Constructeurs jamais appel\u00e9s.<\/strong> Dans l&rsquo;exemple d&rsquo;h\u00e9ritage multiple, <code>MyClassC(MyClassA, MyClassB)<\/code> d\u00e9finit son propre <code>__init__()<\/code> sans appeler ceux de ses parents : ils ne s&rsquo;ex\u00e9cutent donc pas. Pense \u00e0 <code>super().__init__()<\/code> quand tu veux le comportement du parent.<\/p>\n<\/li>\n<li>\n<p><strong>H\u00e9ritage multiple : l&rsquo;ordre compte.<\/strong> Quand deux parents d\u00e9finissent la m\u00eame m\u00e9thode, Python la cherche de gauche \u00e0 droite dans la liste des parents. Avec <code>MyClassC(MyClassA, MyClassB)<\/code>, c&rsquo;est celle de <code>MyClassA<\/code> qui gagne ; inverse l&rsquo;ordre et le r\u00e9sultat bascule. Le module <code>inspect<\/code> te montre cet ordre de r\u00e9solution (le MRO) avec <code>inspect.getmro(MaClasse)<\/code>.<\/p>\n<\/li>\n<li>\n<p><strong>Toujours passer par <code>iter()<\/code>.<\/strong> Appeler directement <code>__iter__()<\/code> ne d\u00e9clenche aucune v\u00e9rification, alors que la fonction <code>iter()<\/code> l\u00e8ve une <code>TypeError<\/code> si <code>__iter__()<\/code> ne renvoie pas un vrai it\u00e9rateur.<\/p>\n<\/li>\n<li>\n<p><strong>L&rsquo;addition n&rsquo;est pas sym\u00e9trique.<\/strong> <code>myobj1 + 7<\/code> fonctionne gr\u00e2ce \u00e0 <code>__add__()<\/code>, mais <code>7 + myobj1<\/code> \u00e9choue tant que tu n&rsquo;as pas \u00e9crit <code>__radd__()<\/code>. M\u00eame principe pour <code>__sub__()<\/code>\/<code>__rsub__()<\/code> et <code>__mul__()<\/code>\/<code>__rmul__()<\/code>.<\/p>\n<\/li>\n<li>\n<p><strong>Ne compte pas sur <code>__del__()<\/code>.<\/strong> Son ex\u00e9cution n&rsquo;est pas garantie : pour lib\u00e9rer un fichier ou une connexion, ferme-les explicitement.<\/p>\n<\/li>\n<\/ul>\n\n\n<figure data-wp-context=\"{&quot;imageId&quot;:&quot;6adef4717182b&quot;}\" data-wp-interactive=\"core\/image\" data-wp-key=\"6adef4717182b\" class=\"wp-block-image size-large wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"2400\" height=\"1650\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on--click=\"actions.showLightbox\" data-wp-on--load=\"callbacks.setButtonStyles\" data-wp-on--pointerdown=\"actions.preloadImage\" data-wp-on--pointerenter=\"actions.preloadImageWithDelay\" data-wp-on--pointerleave=\"actions.cancelPreload\" data-wp-on-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/09\/b1_ch04_heritage_multiple_mro.png\" alt=\"H\u00e9ritage multiple : ClassC h\u00e9rite de ClassA1, ClassA2 et ClassA3 ; la recherche va de gauche \u00e0 droite et c'est A2.func() qui est appel\u00e9e\" class=\"wp-image-137\" srcset=\"https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/09\/b1_ch04_heritage_multiple_mro.png 2400w, https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/09\/b1_ch04_heritage_multiple_mro-300x206.png 300w, https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/09\/b1_ch04_heritage_multiple_mro-1024x704.png 1024w, https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/09\/b1_ch04_heritage_multiple_mro-768x528.png 768w, https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/09\/b1_ch04_heritage_multiple_mro-1536x1056.png 1536w, https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/09\/b1_ch04_heritage_multiple_mro-2048x1408.png 2048w\" sizes=\"auto, (max-width: 2400px) 100vw, 2400px\" \/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\tdata-wp-bind--aria-label=\"state.thisImage.triggerButtonAriaLabel\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"state.thisImage.buttonRight\"\n\t\t\tdata-wp-style--top=\"state.thisImage.buttonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><figcaption class=\"wp-element-caption\">Sur ce sch\u00e9ma, la recherche s&rsquo;arr\u00eate \u00e0 la premi\u00e8re classe, en partant de la gauche, qui d\u00e9finit vraiment la m\u00e9thode demand\u00e9e : ici <code>A2<\/code>.<\/figcaption><\/figure>\n\n\n<h2>Aller plus loin<\/h2>\n<ul>\n<li><strong><code>__init__<\/code> et sa fratrie<\/strong> : trois m\u00e9thodes encadrent la vie d&rsquo;un objet \u2014 <code>__new__()<\/code> le cr\u00e9e, <code>__init__()<\/code> l&rsquo;initialise, <code>__del__()<\/code> intervient \u00e0 sa destruction.<\/li>\n<li><strong>Le protocole d&rsquo;it\u00e9ration<\/strong> : c&rsquo;est le m\u00e9canisme qui se cache derri\u00e8re toute boucle <code>for<\/code>, et que ta classe peut impl\u00e9menter \u00e0 son tour.<\/li>\n<li><strong>Les collections d&rsquo;objets<\/strong> : une classe qui encapsule une liste d&rsquo;objets \u00ab valeur \u00bb (des employ\u00e9s, par exemple) est un motif que l&rsquo;on retrouve dans la plupart des applications r\u00e9elles.<\/li>\n<li><strong>Tes propres structures de donn\u00e9es<\/strong> : liste cha\u00een\u00e9e, file de priorit\u00e9\u2026 rien n&#8217;emp\u00eache de les construire avec des classes personnalis\u00e9es.<\/li>\n<\/ul>\n<h2>\u00c0 retenir<\/h2>\n<ul>\n<li>Une classe range au m\u00eame endroit un \u00e9tat et les op\u00e9rations qui le modifient : tu donnes des ordres \u00e0 un objet au lieu de faire circuler des variables.<\/li>\n<li>Ce que tu sais des fonctions (arguments, valeurs par d\u00e9faut, retours) s&rsquo;applique tel quel aux m\u00e9thodes.<\/li>\n<li>Les m\u00e9thodes magiques rendent tes objets compatibles avec les op\u00e9rateurs, les comparaisons et les boucles de Python.<\/li>\n<li>L&rsquo;h\u00e9ritage r\u00e9utilise une classe ; le duck typing permet de ne pas en avoir besoin quand seule l&rsquo;interface compte.<\/li>\n<li>Python te fait confiance : priv\u00e9 et prot\u00e9g\u00e9 sont des conventions, pas des barri\u00e8res.<\/li>\n<\/ul>","protected":false},"excerpt":{"rendered":"<p>Tes scripts Python ne sont faits que de fonctions ? D\u00e9couvre comment passer \u00e0 la programmation orient\u00e9e objet : classes, m\u00e9thodes, h\u00e9ritage, polymorphisme.<\/p>\n","protected":false},"author":1,"featured_media":267,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11],"tags":[],"class_list":["post-138","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-python"],"_links":{"self":[{"href":"https:\/\/www.ranksignal.fr\/blog\/wp-json\/wp\/v2\/posts\/138","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.ranksignal.fr\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.ranksignal.fr\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.ranksignal.fr\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ranksignal.fr\/blog\/wp-json\/wp\/v2\/comments?post=138"}],"version-history":[{"count":2,"href":"https:\/\/www.ranksignal.fr\/blog\/wp-json\/wp\/v2\/posts\/138\/revisions"}],"predecessor-version":[{"id":140,"href":"https:\/\/www.ranksignal.fr\/blog\/wp-json\/wp\/v2\/posts\/138\/revisions\/140"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.ranksignal.fr\/blog\/wp-json\/wp\/v2\/media\/267"}],"wp:attachment":[{"href":"https:\/\/www.ranksignal.fr\/blog\/wp-json\/wp\/v2\/media?parent=138"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.ranksignal.fr\/blog\/wp-json\/wp\/v2\/categories?post=138"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.ranksignal.fr\/blog\/wp-json\/wp\/v2\/tags?post=138"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}