python objets

Identification

Infoforall

22 - Création d'objets avec Python


Nous allons aujourd'hui voir comment utiliser les classes d'objets dans le cadre de la programmation. Vous les avez déjà rencontré plus d'une fois sans forcément vous en rendre compte. En réalité, dans Python, tout est objet ou presque.

Nous allons donc définir aujourd'hui les termes suivants : objet, classe, attribut, méthode...

Prérequis : savoir utiliser Python :o)

Logiciel nécessaire pour l'activité : Python 3 : Thonny, IDLE ...

Evaluation ✎ : questions 07-08-09-10-12-14-17-18-20

1 - Rappel pour percevoir ce qu'est un objet

Cette partie est destinée à être lue et commentée au tableau.

Vous avez déjà manipulé plusieurs fois des objets, puisque vous avez utilisé des méthodes : une méthode est une fonction particulière : c'est une fonction qui est intégrée dans un objet.

Vous savez que Python comporte des types natifs : int, float, string, dict, tuple...

Voyons d'abord les types natifs les plus simples : int, float.

01° Ce type de contenu (int ou float) est-il

  1. un contenu simple (une simple suite de bits qui permet de retrouver la valeur stockée) ou
  2. un contenu complexe (contenant de multiples choses et nécessitant une structure de données particulières) ?
>>> a = 5 >>> b = 9 >>> c = 5.2

...CORRECTION...

Vous avez vu dans les activités de la partie DONNEES, qu'on peut encoder

  • un integer comme une simple suite d'octets (un seul pour un nombre entier naturel compris entre 0 et 255)
  • un float comme une suite d'octets (4 octets pour le type simple précision, 8 octets pour le type double précision)

A priori, ce type de contenu est donc plutôt un contenu simple ne nécessitant pas de structure de données.

Nous avons vu que lorsqu'on affecte ce type de contenu à une variable, on pouvait d'abord le voir de cette façon : de simples boîtes.

>>> a = 5 >>> b = 9
représentation en boîte

En réalité, nous avons vu depuis qu'il s'agissait d'une suite de bits (5 peut être stocké sous la forme 0000 0101 en binaire non signé par exemple) mais également que la variable ne faisait pas directement référence au contenu. Il s'agit juste d'un alias permettant d'accéder au contenu :

espace des noms

En réalité, ça ne peut pas être aussi simple, il manque au moins une information : comme décoder les bits qui sont présents à cet endroit de la mémoire.

02° Quel est l'entier encodé par l'octet 1111 1111 en base 2 si on sait qu'il s'agit d'un entier non signé ?

...CORRECTION...

Il suffit de se souvenir que chaque case encode une puissance de 2 en partant de 20 à droite, sur le bit de poids faible.

Nombre M =11111111
Les bits codent 1286432168421
On obtient donc1286432168421

D'ou la valeur suivante en base 10 : M = 128+64+32+16+8+4+2+1 = 255.

03° Quel est l'entier encodé par l'octet 1111 1111 en base 2 si on sait qu'il s'agit d'un entier signé ?

Aide : complément à deux.

...CORRECTION...

1er méthode : on se souvient comme ça fonctionne.

Le bit de gauche est le bit de signe : 0 positif, 1 négatif.

Lorsqu'il est positif, on lit le nombre 'normalement' (voir question précédente).

Sinon, on applique la méthode du complément à deux :

  1. On inverse tous les bits (0 en 1, 1 en 0)
  2. On rajoute 1
  3. On lit ce nombre comme un nombre positif en sachant qu'il est négatif.

Ca donne :

  • Bits en mémoire : 1111 1111
  • Etape 1 (inversion) : 0000 0000
  • Etape 2 (rajout de 1) : 0000 0001
  • Interprétation : on lit 1, le code initial encode donc le nombre -1.

2e méthode : on soustrait 28 (puisqu'on utilise un octet) à la valeur brute de l'octet lu comme un entier non signé

  • Bits en mémoire : 1111 1111 : 255 si on considère qu'il s'agit d'un entier non signé
  • 255 - 256 = -1

Si on reprend l'histoire de nos variables, on voit qu'un alias doit faire référence à une zone mémoire qui doit permettre de connaître au moins le type de données stockées (entier signé ou pas, flottant, caractère...), la taille de la zone de stockage allouée (1 octet, 2 octets, 4 octets...) et le contenu des bits eux-mêmes.

variables

Comme vous le voyez, il s'agit donc d'une structure qui ressemble à une sorte de tableau mais :

  1. les éléments n'ont pas à être du même type
  2. les éléments n'ont pas à être de même taille

Bref, c'est pas un tableau.

En C, on pourrait créer une telle structure en utilisant une structure de données nommées ... struct.

Première conclusion temporaire : on voit qu'il y a nécessité de créer des structures de données contenant plusieurs choses n'ayant pas toutes les mêmes types, même pour les données les plus simples.

Continuons en regardant les types natifs faisant référence à des structures de données plus complexes, comme les types dict (dictionnaires) ou les types list (que nous utilisions pour faire des tableaux).

Regardons le type list.

04° Taper les instructions suivantes dans votre console Python pour voir les réponses affichées.

Quelle est la codification qui permet d'activer des fonctions intégrées de base à l'intérieur de mon_tableau ?

Quel est le nom qu'on donne à ces fonction particulières intégrées dans les objets ?

>>> mon_tableau = [10,20,30] >>> type(mon_tableau) >>> type(mon_tableau.append) >>> mon_tableau.append(40) >>> mon_tableau >>> mon_tableau.append(15) >>> mon_tableau >>> mon_tableau.sort() >>> mon_tableau >>> mon_tableau.sort(reverse=True) >>> mon_tableau

...CORRECTION...

>>> mon_tableau = [10,20,30] >>> type(mon_tableau) <class 'list'> >>> type(mon_tableau.append) <class 'builtin_function_or_method'> >>> mon_tableau.append(40) >>> mon_tableau [10, 20, 30, 40] >>> mon_tableau.append(15) >>> mon_tableau [10, 20, 30, 40, 15] >>> mon_tableau.sort() >>> mon_tableau [10, 15, 20, 30, 40] >>> mon_tableau.sort(reverse=True) >>> mon_tableau [40, 30, 20, 15, 10]

Pour trouver les fonctions intégrées aux listes, il suffit de taper le nom de la variable faisant référence à un type list, de taper un point et de nommer la fonction qu'on y cherche.

Comment se nomment ces fonctions présentes à l'intérieur des objets ? Des méthodes !

Les méthodes utilisées ici sur un objet de type list sont :

  • append : elle permet de rajouter un élément à la fin de la variable. Il s'agit donc d'un tableau créé dynamiquement puisqu'un tableau est normalement de taille fixe.
  • sort : elle permet de trier la liste sur laquelle elle s'applique. Elle ne renvoie rien, elle trie sur place la liste. On notera la présence du paramètre nommé reverse, un booléen qui permet de dire si on veut trier en ordre décroissant (True) ou croissant (False, valeur par défaut).

conclusion temporaire : voilà ce qu'est un objet. Une structure de donnée qui contient

  • Des variables, qu'on nomme des attributs
  • Des fonctions, qu'on nomme des méthodes

Maintenant que vous voyez qu'un objet est une structure de données contenant d'autres variables et des méthodes, voyons comment en créer.

2 - Création d'un objet

Lorsqu'on veut créer un objet dans la vraie vie, on a besoin d'une notice, d'un plan, d'une recette.

Si vous avez la recette pour faire un gateau, vous serez capable d'en créer autant que vous le voulez.

C'est pareil en informatique.

Les explications sur la structure à créer se nomment une classe. Et à l'aide d'une classe, on peut créer un objet.

05° Mettre le code suivant en mémoire. Utiliser ensuite les instructions indiquées dans le Shell. Il permet de déclarer une classe nommée Personnage. Et à l'aide de cette classe, on parvient à créer plusieurs objets basés sur la même structure.

1 2
class Personnage : "Ceci est une classe permettant de créer un personnage dans mon super RPG"
>>> Personnage <class '__main__.Personnage'> >>> bob_skywalker = Personnage() >>> bob_skywalker <__main__.Personnage object at 0x7f25459ff9b0> >>> encore_un_skywalker = Personnage() >>> encore_un_skywalker <__main__.Personnage object at 0x7f25459ff898>

Questions

  1. Quelle est la particularité du nom de la classe (qui devrait vous sauter aux yeux) ?
  2. Comment se nomment les deux variables qui contiennent des objets basés sur cette classe ?
  3. Quelles sont les deux lignes qui permettent de créer des objets basés sur la classe Personnage ?
  4. Les deux objets font-ils référence à la même zone mémoire ?

...CORRECTION...

  • Quelle est la particularité du nom de la classe (qui devrait vous sauter aux yeux) ?
    • Le nom commence par une majuscule. Une majuscule ! Personnage et pas personnage.
  • Comment se nomment les deux variables qui contiennent des objets basés sur cette classe ?
    • On crée visiblement deux variables par affectation : bob_skywalker et encore_un_skywalker
  • Quelles sont les deux lignes qui permettent de créer des objets basés sur la classe Personnage ?
    • On crée des objets basés sur la classe Personnage en utilisant une fonction qui porte le nom de la classe utilisée (avec une majuscule aussi du coup). Par contre, s'agissant d'une fonction, on retrouve des paranthèses.
    • >>> bob_skywalker = Personnage() >>> encore_un_skywalker = Personnage()
  • Les deux objets font-ils référence à la même zone mémoire ?
    • Non, on voit lorsqu'on demande à la console d'afficher ces variables qu'elle ne donne pas visuellement le "contenu" mais juste le type (ici des objets basés sur la classe Personnage) et leurs identifiants-mémoires qui désignent bien deux zones différentes.
    • >>> bob_skywalker <__main__.Personnage object at 0x7f25459ff9b0> >>> encore_un_skywalker <__main__.Personnage object at 0x7f25459ff898>
Vocabulaire : Classe, Constructeur, objet et instance

Si on désire créer une classe, on utilise donc le mot-clé class qui s'utilise comme le mot-clé def pour créer une fonction. N'oubliez pas le : final.

Le nom d'une classe commence par une Majuscule par convention. Rien d'obligatoire mais tout le monde respecte cette convention qui permet de rendre le code lisible facilement.

1
class Personnage :

Pour créer un objet, on a besoin d'utiliser un Constructeur. En Python, il porte le même nom que la classe sur laquelle on se base. On rajoute juste les parenthèses.

bob_skywalker = Personnage() encore_un_skywalker = Personnage()

Les objets créés à partir d'une classe ont ensuite une existence propre. On dit qu'ils sont des instances de la classe.

Ici, bob_skywalker et encore_un_skywalker sont des objets : ceux sont des instances de la classe Personnage.
Ils ont été créés en utilisant le constructeur Personnage().

Voici pour l'introduction. Retenez donc qu'une classe correspond aux plans permettant de définir une chose (les plans techniques d'une voiture par exemple) et que l'objet correspond à une chose qu'on a réellement créé à partir de ces plans.

On peut ainsi créer plusieurs voitures différentes (des objets) à partir d'un même plan (une Classe).

Il nous reste à voir comment remplir nos classes avec des variables (qu'on nommera des attributs) et des fonctions (qu'on nommera des méthodes).

3 - Rajouter des attributs à la volée

Pour l'instant, la classe Personnage est une moule vide. Elle se sert à rien et ne contient rien de particulier.

Il est temps de donner un peu de relief à nos personnages.

Pour cela, nous allons commencer par rajouter des variables attachées à nos objets. On nomme ces variables des attributs.

Commençons par voir comment créer des attributs (ou variables d'instance) à la volée. Mais attention : sachez que c'est mal : c'est pour cela que nous ne le ferons plus ensuite.

Création d'attributs à la volée

Dans beaucoup de langages permettant la programmation objet, il n'est pas possible de rajouter des attributs après l'instanciation de l'objet.

Avec Python, c'est possible. Regardons comment ça fonctionne :

1 2 3 4 5 6 7 8 9 10 11 12 13 14
# Déclaration des classes class Personnage : "Ceci est une classe permettant de créer un personnage dans mon super RPG" # Programme principal si on lance directement le fichier if __name__ == "__main__" : bob = Personnage() bob.nom = "Skywalker" bob.prenom = "Bob" bob.age = 25

Que fait ce programme ? Il crée un objet bob (ligne 11).

Ensuite, on lui rajoute des attributs en utilisant la ponctuation que nous avons déjà vu de nombreuses fois dans d'autres activités : on place le nom de l'objet, on rajoute un point et on rajoute le nom de l'attribut (ou variable d'instance).

Ici, on a trois affectations (lignes 12-13-14).

C'est comme si on avait rajouté trois variables à l'intérieur de l'objet.

Si on lance ce programme, on pourra alors visualiser le contenu dans le Shell, voire les modifier.

>>> bob <__main__.Personnage object at 0x7f3ca24372b0> >>> bob.nom 'Skywalker' >>> bob.prenom 'Bob' >>> bob.nom = "Bond" >>> bob.nom 'Bond'

La première ligne permet de vérifer que l'objet existe bien.

Ensuite on lit le contenu des attributs nom et prenom.

On modifie ensuite le nom et on vérifie que la modification a bien été prise en compte.

06° Utiliser le programme puis les commandes Shell pour vérifier que cela fonctionne.

✎ 07° Rajouter un attribut classe (contenant par exemple "Jedi" ou "Chasseur de Primes") et un attribut niveau.

Structure d'un objet

Un objet possède donc une adresse.

A cette adresse se trouve une table listant les alias des attributs et l'identifiant-mémoire qui correspond.

structure d'un objet

La codification bob.niveau veut donc dire en Français : va voir à l'adresse de cet objet et trouve le contenu qui correspond à cet attribut.

structure d'un objet

On retrouve donc la possibilité :

  • De lire le contenu d'un attribut : bob.niveau
  • De modifier le contenu d'un attribut avec une affectation du type : bob.niveau = 5

✎ 08° Donner les instructions (et leurs résultats) de façon à

  1. Afficher le niveau du personnage
  2. Afficher l'identifiant du personnage
  3. Afficher l'identifiant du niveau du personnage
  4. Modifier le niveau du personnage
  5. Afficher le nouveau niveau du personnage
  6. Afficher l'identifiant du niveau du personnage

✎ 09° Un objet est-il mutable ? On rappelle que cela veut dire qu'on peut modifier le contenu de la structure sans modifier l'adresse de l'objet lui-même.

✎ 10° Lorsqu'on modifie un attribut par affectation directe, cela modifie-t-il son identifiant-mémoire ?

4 - Etapes du Constructeur

En peu de théorie : comment fonctionne le Constructeur ?

Etapes lors de l'instanciation

Lorsqu'on crée un objet à partir d'une Classe, on dit qu'on réalise une instanciation.

On peut donc dire qu'un objet est une instance d'une Classe donnée.

Pour créer une instance, il faut faire appel à un Constructeur : il porte le même nom que la classe, il suffit de rajouter les parenthèses derrière. Exemple :

>>> bob_skywalker = Personnage()

Ce Constructeur va alors créer la structure de données qui permettra de contenir les attributs et les méthodes de la Classe.

  1. On commence par créer l'objet. En Python, c'est fait automatiquement en utilisant le nom de la classe comme fonction. Après cette étape, l'objet est créé et possède une adresse-mémoire. Remarque : vous pourriez modifier la création par défaut en utilisant la méthode spéciale __new__. Nous ne parlerons pas de cette méthode __new__ dans ce cours car comprendre l'intérêt de modifier la création par défaut réclame des connaissances qui ne sont pas au programme.
  2. Une fois l'objet créé, la méthode spéciale __init__ est activée automatiquement si le créateur de la classe en a inséré une dans le code. Elle permettra d'initialiser les variables internes de l'objet qu'on nommera variables d'instance ou attributs.
  3. Le Constructeur renvoie l'adresse de l'objet qui vient d'être créé et configuré par l'initialisateur. La variable de réception contient donc la référence de cet objet et on pourra agir dessus.

Si on résume :

On commence par lancer le constructeur.

>>> bob_skywalker = Personnage()

Le constructeur renvoie avec un return l'identifiant-mémoire de l'objet (ici 0x7f3ca24372b0).

>>> bob_skywalker = 0x7f3ca24372b0

On affecte cette adresse à la variable bob_skywalker.

>>> bob_skywalker = 0x7f3ca24372b0

A partir de là, on peut donc créer ou modifier les attributs stockés dans notre objet en tapant par exemple :

>>> bob_skywalker.nom = "skywalker"

Si vous voulez juste lire le contenu d'un attribut :

>>> bob_skywalker.nom
Méthodes spéciales

Les méthodes dont le nom commence et finit par deux tirets sont nommées des variables spéciales. Ce sont des méthodes que Python utilise pour jouer un rôle particulier. On peut les redéfinir à la main de façon à modifier le comportement par défaut du système.

Quelques exemple :

  • Méthode permettant de transformer le constructeur : __new__
  • Méthode permettant permettant au programmeur d'initialiser un objet nouvellement créé : __init__
  • Méthode permettant au programmeur d'expliquer comment gérer l'addition de deux objets de même type : __add__
  • Méthode permettant au programmeur d'expliquer comment gérer l'affichage lorsqu'on demande à voir l'objet : __str__
  • ...

5 - Méthode __init__

Voyons maintenant à quoi sert la méthode spéciale __init__.

Le problème du rajout à la volée est qu'on peut finir par écraser le contenu d'un attribut qui existe déjà.

Même si Python permet de rajouter des attributs à la volée n'importe où dans le code, on veillera à ne pas le faire.

C'est ici qu'intervient l'initialisation via la méthode __init__.

On va y placer tous les attributs qu'on veut créer, avec une valeur initiale. Quelqu'un voulait modifier le code en rajoutant un attribut peut ainsi aller voir si un attribut ayant ce nom existe déjà.. Cela lui évitera de créer un nouveau code écrasant votre attribut !

11° Utiliser le code ci-dessous puis les commandes Shell pour visualiser cette initialisation.

1 2 3 4 5 6 7 8
class Personnage : "Ceci est une classe permettant de créer un personnage dans mon super RPG" def __init__(self) : self.nom = 'aucun' self.prenom = 'aucun' self.niveau = 0 self.classe = 'aucune'
>>> bob = Personnage() >>> bob.nom 'aucun' >>> bob.prenom 'aucun' >>> bob.niveau 0 >>> bob.classe 'aucune' >>> bob.prenom = 'bob' >>> bob.prenom 'bob'

Question : en regardant les attributs déjà créés, serait-il intelligent de vouloir créer un attribut contenant le niveau de magie en le nommant niveau ? Ben non. Un attribut ayant ce nom existe déjà.

✎ 12° Que va être le nom du personnage suivant si on exécute cette instruction ?

>>> zzz = Personnage() >>> zzz.nom

13° Que contient le paramètre self ? Utiliser le code ci-dessous pour le découvrir.

Rappel : la fonction hex permet d'exprimer l'adresse en hexadécimal plutôt qu'en décimal.

1 2 3 4 5 6 7 8 9
class Personnage : "Ceci est une classe permettant de créer un personnage dans mon super RPG" def __init__(self) : self.nom = 'aucun' self.prenom = 'aucun' self.niveau = 0 self.classe = 'aucune' print(f"Depuis la fonction __init__, le paramètre self contient {self}")
>>> a = Personnage() Depuis la fonction __init__, le paramètre self contient
<__main__.Personnage object at 0x7fc9c389aba8>
>>> hex(id(a)) '0x7fc9c389aba8'

...CORRECTION...

On remarque que self correspond à l'identifiant-mémoire de l'objet lui-même.

En utilisant self, on a donc accès à l'objet comme si on avait tapé le nom de la variable qui lui fait référence.

Paramètre self

Vous avez vu que self contient l'adresse de l'objet qui est en train d'activer la méthode.

Lors de l'appel de la méthode spéciale __init__, ce paramètre va automatiquement contenir l'adresse de l'objet sur lequel nous sommes en train d'agir.

Imaginons qu'on lance ceci :

>>> a = Personnage()

Que se passe-t-il ?

  1. Le constructeur crée un nouvel objet basé sur la classe Personnage et le système fournit un identifiant-mémoire libre à notre nouvel objet, disons l'identifiant 42.
  2. Le constructeur lance de lui-même l'appel à la méthode spéciale __init__ et lui fournit automatiquement la valeur du paramètre self : il reçoit l'identifiant 42. Elle va donc agir sur les attributs de l'objet situé à cette adresse.
  3. Finalement, le constructeur renvoie l'identifiant de l'objet (42) vers le programme. Il peut alors être stocké dans la variable a.

✎ 14° Lors de l'appel automatique à __init__ sur la première commande du Shell, que va contenir le paramètre self ? L'identifiant-mémoire de a ou l'adresse de b ?

>>> a = Personnage() >>> b = Personnage()

Même question avec la deuxième instruction visible dans la console de Python.

1 2 3 4 5 6 7 8 9
class Personnage : "Ceci est une classe permettant de créer un personnage dans mon super RPG" def __init__(self) : self.nom = 'aucun' self.prenom = 'aucun' self.niveau = 0 self.classe = 'aucune' print(f"Depuis la fonction __init__, le paramètre self contient {self}")

Bien entendu, il n'est pas très pratique d'avoir à initialiser une deuxième fois les attributs pour leur donner la bonne valeur.

Heureusement, le Constructeur fonctionne comme n'importe quelle autre fonction : il peut recevoir des arguments qu'il placera dans des paramètres.

15° Utiliser le code ci-dessous qui permet de créer une liaison entre le Constructeur et la méthode spéciale __init__.

1 2 3 4 5 6 7 8
class Personnage : "Ceci est une classe permettant de créer un personnage dans mon super RPG" def __init__(self, a, b, c, d) : self.nom = a self.prenom = b self.niveau = c self.classe = d
>>> bob = Personnage("Luke", "Skywalker", 5, "Jedi") >>> bob.nom 'Luke'

On voit donc ici que :

  1. Paramètre 1 : self. On ne le fournit pas d'agument. Il sera automatiquement affectée avec l'identifiant-mémoire de l'objet bob.
  2. Paramètre 2 : a. Il reçoit l'argument-string "Luke", qui est normalement le prénom du personnage.
  3. Paramètre 3 : b. Il reçoit l'argument-string "Skywalker", qui est normalement le nom du personnage.
  4. Paramètre 4 : c. Il reçoit l'argument-entier 5, qui est le niveau du personnage.
  5. Paramètre 5 : d. Il reçoit l'argument-string "Jedi", qui est sans doute la classe du personnage.

16° Expliquer le problème visualisé sur le nom du personnage en répondant à ceci :

  • Quel est le nom du paramètre qui reçoit l'argument "Luke" (un prénom) ? Est-ce logique lorsqu'on regarde ensuite les lignes 5 et 6 ?
  • Quel est le nom du paramètre qui reçoit l'argument "Skywalker" (un nom) ?
  • Quel est le nom du paramètre qui reçoit l'argument 5 ?
  • Quel est le nom du paramètre qui reçoit l'argument "Jedi" ?
  • Le paramètre self n'a pas été transmis lors de l'utilisation du Constructeur Personnage() dans la console. Est-ce une erreur ?

...CORRECTION...

4
def __init__(self, a, b, c, d) :
>>> bob = Personnage("Luke", "Skywalker", 5, "Jedi")
  • Quel est le nom du paramètre qui reçoit l'argument "Luke" ?
  • On le stocke dans a. Pas très logique puisque a sert ensuite à initialiser l'attribut nom...

  • Quel est le nom du paramètre qui reçoit l'argument "Skywalker" ?
  • On le stocke dans b

  • Quel est le nom du paramètre qui reçoit l'argument 5 ?
  • On le stocke dans c

  • Quel est le nom du paramètre qui reçoit l'argument "Jedi" ?
  • On le stocke dans d

  • Le paramètre self n'est pas transmis, est-ce une erreur ?
  • Non : c'est le Constructeur lui-même qui va automatiquement renvoyer l'indentifiant-mémoire de l'objet qu'il vient de créer.

Alors où est le problème ? Au fait que le paramètre a contient un prénom envoyé par l'utilisateur, or on le stocke ligne 5 dans l'attribut nom !

✎ 17° Corriger le programme de façon à bien mettre le nom dans l'attribut nom et le prénom dans l'attribut prénom.

Dernière chose à propos de la méthode __init__ (et globalement des fonctions) : son alias est comme les autres alias juste un raccourci vers une zone mémoire. La différence ? On y trouve du code à exécuter.

>>> bob.__init__ <bound method Personnage.__init__ of <__main__.Personnage object at 0x7f90d24d7cf8>>

On peut donc voir les objets comme des conteneurs : ils permettent de stocker

  • des variables (les fameux attributs)
  • des fonctions (les fameuses méthodes).
objet : conteneur à variables et fonctions

6 - Paramètres nommés et paramètres par défaut

Première astuce : vous la connaissez. Choisir des noms de variables explicites. Franchement a, b, c, d, e...

Voici un premier exemple qui permettra plus facilement à la personne qui utilise votre code de se rendre compte qu'on attend d'avoir le nom puis le prénom :

1 2 3 4 5 6 7 8
class Personnage : "Ceci est une classe permettant de créer un personnage dans mon super RPG" def __init__(self, nom, pre, niv, cla) : self.nom = nom self.prenom = pre self.niveau = niv self.classe = cla

Dans la première version, j'ai choisi de ne pas mettre exactement le même nom pour le paramètre et pour l'attribut. Mais on a le droit. Voici le même exemple en utilisant exactement le même nom à chaque fois.

1 2 3 4 5 6 7 8
class Personnage : "Ceci est une classe permettant de créer un personnage dans mon super RPG" def __init__(self, nom, prenom, niveau, classe) : self.nom = nom self.prenom = prenom self.niveau = niveau self.classe = classe

✎ 18° Fournir l'instruction à écrire pour créer un personnage nommé Loulou Skydroper, un Technicien niveau 12. Attention, Loulou, c'est le prénom. On considère que la classe Personnage est l'une des deux précédentes.

Comme l'ordre est parfois un peu difficile à imposer, on peut également faire l'appel à une fonction en donnant les noms des paramètres qu'on veut voir associer à un argument.

Ca fonctionne avec toutes les fonctions, pas uniquement avec la fonction Constructeur.

>>> bob = Personnage(prenom="Luke", nom="Skywalker", niveau=5, classe="Jedi") >>> jim = Personnage(classe="Capitaine de l'Enterprise", nom="Kirk", niveau=6, prenom="James")

19° Utiliser la classe Personnage utilisant des paramètres nommés. Utiliser les exemples de création ci-dessus pour vérifier que cela fonctionne bien. Question : lorsqu'on utilise des paramètres nommés (c'est à dire qu'on donne le nom du paramètre qui doit contenir l'argument qu'on envoie), est-on obligé de respecter l'ordre des paramètres fourni dans le code de la fonction ?

...CORRECTION...

Non. Puisqu'on donne le nom du paramètre à chaque fois, pas la peine de fournir les paramètres dans un ordre précis.

Convention sur les paramètres

Mais... mais... il n'y a pas d'espace autour du = lorsqu'on envoie nos paramètres nommés :

>>> bob = Personnage(prenom="Luke", nom="Skywalker", niveau=5, classe="Jedi")

C'est la convention sur les paramètres contrairement aux déclarations de variables habituelles.

C'est comme ça.

Dernière chose : on peut imposer des valeurs par défaut à nos fonctions. Il suffit de fournir la valeur par défaut associée au paramètre si on n'en fournit pas lors de l'appel.

1 2 3 4 5 6 7 8
class Personnage : "Ceci est une classe permettant de créer un personnage dans mon super RPG" def __init__(self, nom='Aucun', prenom='Aucun', niveau=0, classe="Humain de base") : self.nom = nom self.prenom = prenom self.niveau = niveau self.classe = classe

✎ 20° Lancer la création d'un personnage sans donner sa classe de personnage. En regardant le code, deviner la valeur qui va être associée à la classe de personnage.

>>> jim = Personnage(prenom="Toto", nom="l'Asticot", niveau=10) >>> jim.classe '???'

7 - FAQ

Et qu'est-ce qui me prouve que __new__ arrive avant __init__ ?

Voici un code qui permet de demander d'afficher un message lors de l'appel de la méthode __new__ et de la méthode __init__. Il vous permettra de voir que la première méthode est bien activée avant la deuxième.

1 2 3 4 5 6 7 8 9
class Personnage : "Ceci est une classe permettant de créer un personnage dans mon super RPG" def __new__(self) : print("On est dans __new__ : on va créer l'objet") return super(Personnage, self).__new__(self) def __init__(self): print("On est donc __init__' : on va initialiser les attributs de l'objet")

Voici le résultat d'une des utilisations :

>>> a = Personnage() On est dans __new__ : on va créer l'objet On est donc __init__' : on va initialiser les attributs de l'objet

Voilà, on voit bien que la méthode __new__ est activée d'abord lorsqu'on crée un objet.

Si la ligne 6 ne vous a pas calmer, vous pouvez me poser des questions pour savoir ce qu'elle veut dire :o)

Nous avons fait la première partie de cette découverte de la programmation orientée objet.

La Classe est donc le moule permettant de créer des structures de stockage contenant des variables et des fonctions.

Lorsqu'on utilise le Constructeur d'une classe, il renvoie l'adresse de l'objet qu'il vient de créer. On dit que cet objet est une instance de la Classe.

La structure de cet objet a ceci de particulier par rapport à ce qu'on a vu précédemment qu'elle permet de stocker :

  • Des variables d'instance, qu'on nomme également des attributs.
  • Des fonctions, qu'on nomme également des méthodes.

L'activité suivante va maintenant décrire précisement la création de méthode, autre que la méthode spéciale __init__.

Activité publiée le 01 09 2020
Dernière modification : 12 09 2020
Auteur : ows. h.