Nous allons continuer à parler des objets et des Classes. Voici d'abord une correction possible des questions de l'activité précédente :
class Personne(object):
ou class Personnage
?
Si vous cherchez des codes dans une documentation, vous trouverez surement des classes définies à l'aide de class Personnage(object):
. Il s'agit d'une ancienne façon de déclarer les classes en Python 2. Ce terme object fut rajouté suite à une mise à jour de façon à permettre aux classes de Python 2 de se comporter réellement comme de vraies classes. Comme cette déclaration est rétrocompatible en Python 3, on trouve beaucoup de codes qui incluent cette façon de faire. Mais en Python 3, si vous ne notez rien entre les parenthèses, Python réagira comme si vous aviez rajouté object.
En résumé, en Python 3, que vous notiez class Personnage:
ou class Personnage(object):
vous obtiendrez la même chose.
Nous expliquerons d'où vient cet object dans la troisième activité sur les objets.
Pour rappel, ce code avait été initié pour créer une sorte de jeu où deux personnages pouvaient se battre entre eux, le perdant du combat perdant un niveau.
On y trouve donc une classe Personnage qui possède :
nom
, prenom
, niveau
et classe
__init__
pour initialiser correctement toute nouvelle instance de la classe Personnage (avec deux underscores)presentation
pour afficher les caractéristiques du Personnage.perdre_un_niveau
pour diminuer de un le niveau de l'objet sur lequel elle agit.combat(adversaire)
qui simule le combat entre l'objet-Personnage qui y fait appel et un objet-Personnage adverse. La méthode fait perdre un niveau via perdre_un_niveau et renvoie la référence de l'objet-Personnage qui a perdu.#!/usr/bin/env python
# -*- coding: utf-8 -*-
import random
# - - - - - - - - - - - - - - - - - -
# Déclarations des classes
# - - - - - - - - - - - - - - - - - -
class Personnage:
"Ceci est une classe permettant de créer un personnage dans mon super RPG"
def __init__(self) :
self.nom = 'nobody'
self.prenom = 'nobody'
self.niveau = 0
self.classe = 'aucune classe'
def presentation(self) :
"Affiche quelques unes des valeurs stockées dans le personnage"
print(self.nom, ' ',self.prenom)
print(self.classe, ' de niveau ',self.niveau)
def perdre_un_niveau(self) :
"Cette méthode permet de réduire le niveau de 1"
self.niveau +=-1
def combat(self,adversaire) :
"Ceci est une méthode qui renvoie le nom du combattant qui perd"
perdant = ''
test = True
while test :
valeur1 = random.randint(1,6)+self.niveau
valeur2 = random.randint(1,6)+adversaire.niveau
if valeur1>valeur2 :
perdant = adversaire
test = False
elif valeur2>valeur1 :
perdant = self
test = False
perdant.perdre_un_niveau()
return(perdant)
# - - - - - - - - - - - - - - - - - -
# Déclarations des fonctions
# - - - - - - - - - - - - - - - - - -
# - - - - - - - - - - - - - - - - - -
# Corps du programme
# - - - - - - - - - - - - - - - - - -
bob_skywalker = Personnage()
bob_skywalker.nom = "Skywalker"
bob_skywalker.prenom = "Bob"
bob_skywalker.niveau = 1
bob_skywalker.classe = "Jedi raté"
perso2 = Personnage()
perso2.nom = "De nom je n'ai pas"
perso2.prenom = "Yoda"
perso2.niveau = 3
perso2.classe = "Vieux Jedi"
print(bob_skywalker.combat(perso2).nom, " vient de perdre un combat")
print(bob_skywalker.combat(perso2).nom, " vient de perdre un combat")
Nous avions bien progressé puisque maintenant nous n'avons plus à créer de fonctions externes pour gérer un combat entre deux objets de cette classe Personnage : toute la gestion est intégrée dans la définition de la Classe. Si quelqu'un importe cette Classe, les personnages pourront se battre puisque le code gérant l'interaction est déjà inclus. C'est plutôt bien.
Que fait le corps du programme sous ma Classe ? C'est simple :
bob_skywalker = Personnage()
bob_skywalker.nom = "Skywalker"
bob_skywalker.prenom = "Bob"
bob_skywalker.niveau = 1
bob_skywalker.classe = "Jedi raté"
perso2 = Personnage()
perso2.nom = "De nom je n'ai pas"
perso2.prenom = "Yoda"
perso2.niveau = 3
perso2.classe = "Vieux Jedi"
print(bob_skywalker.combat(perso2).nom, " vient de perdre un combat")
print(bob_skywalker.combat(perso2).nom, " vient de perdre un combat")
Nous allons commencer par voir comment cacher le code de la Classe. Ainsi la plupart des utilisateurs iront simplement lire la documentation après avoir importer la Classe plutôt que de modifier directement le code de la Classe.
Nous allons donc créer un fichier Python nommé mes_classess.py qui va contenir la Classe :
01° Créer et enregistrer un fichier mes_classes.py en utilisant le code suivant :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import random
# - - - - - - - - - - - - - - - - - -
# Déclarations des classes
# - - - - - - - - - - - - - - - - - -
class Personnage:
"Ceci est une classe permettant de créer un personnage dans mon super RPG"
def __init__(self) :
self.nom = 'nobody'
self.prenom = 'nobody'
self.niveau = 0
self.classe = 'aucune classe'
def presentation(self) :
"Affiche quelques unes des valeurs stockées dans le personnage"
print(self.nom, ' ',self.prenom)
print(self.classe, ' de niveau ',self.niveau)
def perdre_un_niveau(self) :
"Cette méthode permet de réduire le niveau de 1"
self.niveau +=-1
def combat(self,adversaire) :
"Ceci est une méthode qui renvoie le nom du combattant qui perd"
perdant = ''
test = True
while test :
valeur1 = random.randint(1,6)+self.niveau
valeur2 = random.randint(1,6)+adversaire.niveau
if valeur1>valeur2 :
perdant = adversaire
test = False
elif valeur2>valeur1 :
perdant = self
test = False
perdant.perdre_un_niveau()
return(perdant)
02° Créer et enregistrer au même endroit le fichier prog_princ.py contenant les lignes suivantes :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import mes_classes
# mes classes car le fichier externe se nomme mes_classes.py
# - - - - - - - - - - - - - - - - - -
# Corps du programme
# - - - - - - - - - - - - - - - - - -
bob_skywalker = Personnage()
bob_skywalker.nom = "Skywalker"
bob_skywalker.prenom = "Bob"
bob_skywalker.niveau = 1
bob_skywalker.classe = "Jedi raté"
perso2 = Personnage()
perso2.nom = "De nom je n'ai pas"
perso2.prenom = "Yoda"
perso2.niveau = 3
perso2.classe = "Vieux Jedi"
print(bob_skywalker.combat(perso2).nom, " vient de perdre un combat")
print(bob_skywalker.combat(perso2).nom, " vient de perdre un combat")
03° Executer prog_princ.py pour voir si ça fonctionne. Alors ? Tentez de réparer si vous le pouvez.
...CORRECTION...
Le problème vient des noms : le module se nomme mes_classes (puisqu'on a créé un fichier mes_classes.py)
Or, la classe Personnage se trouve dans le module mes_classes. Il faut donc utiliser bob_skywalker = mes_classes.Personnage()
pour atteindre la méthode CONSTRUCTEUR.
Il faudra faire la même chose pour créer perso2.
La version définitive est donc :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import mes_classes
# mes classes car le fichier externe se nomme mes_classes.py
# - - - - - - - - - - - - - - - - - -
# Corps du programme
# - - - - - - - - - - - - - - - - - -
bob_skywalker = mes_classes.Personnage()
bob_skywalker.nom = "Skywalker"
bob_skywalker.prenom = "Bob"
bob_skywalker.niveau = 1
bob_skywalker.classe = "Jedi raté"
perso2 = mes_classes.Personnage()
perso2.nom = "De nom je n'ai pas"
perso2.prenom = "Yoda"
perso2.niveau = 3
perso2.classe = "Vieux Jedi"
print(bob_skywalker.combat(perso2).nom, " vient de perdre un combat")
print(bob_skywalker.combat(perso2).nom, " vient de perdre un combat")
Il faudra donc modifier vos codes en conséquence.
Sinon, il y a la méthode brute qui permet d'importer le module et de pouvoir l'utiliser directement. Attention néanmoins : il est possible que l'une de vos classes porte le même nom qu'une classe native de Python. A utiliser avec précaution donc :
import mes_classes
A remplacer par
from mes_classes import *
On pourra alors utiliser le module sans précéder ses élements par son nom :
bob_skywalker = Personnage()
Nouvel élément d'étude : comment fonctionne la comparaison et la copie d'un objet par rapport à la copie d'une variable.
04° Regarder le code suivant et réflechir à ce qu'il peut donner comme réponse. Créer ensuite un fichier mon_programme_2.py pour vérifier le résultat.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
a = 45
print("On crée a avec a=45 et l'identifiant de la variable a est ",id(a))
b = 45
print("On crée b avec b=45 et l'identifiant de la variable b est ",id(b))
print(a==b)
input("Appuyer sur ENTREE")
...CORRECTION...
Le programme crée une variable a et y place 45 ( a = 45).
Le programme crée une variable b et y place 45 ( b = 45).
On constate que les deux variables pointent vers la même id : Python pointe vers la même case mémoire pour a et b (case qui contient 45) plutôt que de prendre une case propre pour b. Le test a==b affiche donc True.
On crée a avec a =45 et l'identifiant de la variable a est 1752283584
On crée b avec b =45 et l'identifiant de la variable b est 1752283584
True
05° Faisons la même chose avec deux objets-Personnage (c'est à dire instanciation de la classe Personnage) : on les remplit de la même façon et on tente de les comparer :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import mes_classes
# - - - - - - - - - - - - - - - - - -
# Corps du programme
# - - - - - - - - - - - - - - - - - -
bob_skywalker = mes_classes.Personnage()
bob_skywalker.nom = "Skywalker"
bob_skywalker.prenom = "Bob"
bob_skywalker.niveau = 1
bob_skywalker.classe = "Jedi raté"
print("On crée bob_skywalker et l'identifiant de l'objet est ",id(bob_skywalker))
perso2 = mes_classes.Personnage()
perso2.nom = "Skywalker"
perso2.prenom = "Bob"
perso2.niveau = 1
perso2.classe = "Jedi raté"
print("On crée perso2 et l'identifiant de l'objet est ",id(perso2))
print(bob_skywalker==perso_2)
input("Appuyer sur ENTREE")
...CORRECTION...
Le programme crée un objet nommé bob_skywalker.
Le programme crée un objet nommé perso_2 et le remplit à l'identique du premier.
On constate pourtant que les id sont différentes : Python pointe vers deux adresses différentes. bob_skywalker==perso_2 affiche donc False.
On crée bob_skywalker et l'identifiant de l'objet est 2595884309808
On crée perso_2 et l'identifiant de l'objet est 2595884309304
False
On parle de similitude si deux entités possèdent le même contenu.
Deux objets similaires ne pointent pas vers la même adresse : il ne s'agit pas de la même entité.
Si on teste bob_skywalker == perso2
, on obtient donc False
.
Pourquoi ? Parce qu'on ne teste pas les contenus : on teste les adresses !
Ce n'est pas le cas avec les variables qui pointe vers des contenus typés (integer, float ou string). Dans le cas d'un test a == b
, on aura True
.
Même si ce n'est pas vraiment aussi simple, retenez que :
a == b
teste les id si a et b pointent des "objets"a == b
teste les contenus si a et b pointent des types simples.
06° Faisons la même chose mais l'objet perso_2 est obtenu en utilisant perso_2 = bob_skywalker
:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import mes_classes
# - - - - - - - - - - - - - - - - - -
# Corps du programme
# - - - - - - - - - - - - - - - - - -
bob_skywalker = mes_classes.Personnage()
bob_skywalker.nom = "Skywalker"
bob_skywalker.prenom = "Bob"
bob_skywalker.niveau = 1
bob_skywalker.classe = "Jedi raté"
print("On crée bob_skywalker et l'identifiant de l'objet est ",id(bob_skywalker))
perso2 = bob_skywalker
print("On crée perso2 et l'identifiant de l'objet est ",id(perso2))
print(bob_skywalker==perso_2)
input("Appuyer sur ENTREE")
...CORRECTION...
On crée bob_skywalker avec une nouvelle instanciation de la classe Personnage. bob_skywalker contient donc la référence du nouvel objet.
On crée perso_2 en stockant la référence précédente dans la nouvelle variable aussi. perso_2 pointe donc vers la même référence, elle pointe le même objet que bob_skywalker
On constate cette fois que bob_skywalker == perso_2 affiche True.
On crée bob_skywalker et l'identifiant de l'objet est 2251891350832
On crée perso_2 et l'identifiant de l'objet est 2251891350832
True
On parle d'unicité si deux entités possèdent le même identifiant ou la même adresse mémoire.
Il s'agit de la même entité qu'on peut nommer avec plusieurs alias (ou plusieurs noms).
C'est la même chose avec vous : vous possèdez un nom et très certainement un surnom. Mais qu'on fasse référence à l'un ou l'autre, on pointe bien vers vous.
Si on teste a == b
, on obtient donc True
car a et b ont le même id.
Pour créer un objet disposant de plusieurs alias, il suffit d'affecter le contenu de la variable pointe l'objet dans une nouvelle variable : perso_2 = bob_skywalker
.
Et maintenant, tour de magie !
Oui, enfin ... un petit :
Voici un code qui crée perso_2 à partir de perso_1 : perso_2 = perso_1
.
Il modifie ensuite l'attribut nom
de perso_1 puis l'attribut prenom
de perso_2.
Enfin, il affiche les caractéristiques de perso_1 et de perso_2.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import mes_classes
# - - - - - - - - - - - - - - - - - -
# Corps du programme
# - - - - - - - - - - - - - - - - - -
perso_1 = mes_classes.Personnage()
perso_1.nom = "Skywalker"
perso_1.prenom = "Anakin"
perso_1.niveau = 1
perso_1.classe = "Jedi noir"
print("On crée perso_1 et l'identifiant de l'objet est ",id(perso_1))
perso_2 = perso_1
print("On crée perso_2 et l'identifiant de l'objet est ",id(perso_2))
perso_1.nom = "Darth"
perso_2.prenom = "Vador"
print("\nContenu de perso_1")
perso_1.presentation()
print("\nContenu de perso_2")
perso_2.presentation()
input("Appuyer sur ENTREE")
07° Avant d'essayer le programme, tentez de deviner ce qu'il devrait afficher.
...CORRECTION...
perso_1 et perso_2 sont deux alias du même objet.
Modifier l'attribut nom à travers perso_1 va également changer l'affichage sur perso_2 puisque dans les deux cas on modifie la même zone mémoire.
Modifier l'attribut prenom à travers perso_2 va changer l'affichage sur perso_1.
On crée perso_1 et l'identifiant de l'objet est 1812704861432
On crée perso_2 et l'identifiant de l'objet est 1812704861432
Contenu de perso_1
Darth Vador
Jedi noir de niveau 1
Contenu de perso_2
Darth Vador
Jedi noir de niveau 1
Tout ça à cause de l'unicité : les noms de vos variables-objets sont différents mais en réalité, elles pointent bien les mêmes données. Si vous modifiez les attributs d'un des alias, vous modifiez nécessairement l'autre alias aussi puisque c'est le même objet avec deux noms.
En réalité, on parle souvent de l'objet perso_1 et de l'objet perso_2.
Dans la plupart des programmes simples, chaque variable-objet fait référence à un objet différent. On a donc l'habitude de faire cet abus de langage.
Mais s'il fallait vraiment utiliser un langage précis, il faudrait dire que perso_1 n'est pas l'objet mais simplement la référence vers un objet dont l'id est 1812704861432.
En réalité, cette histoire de références va quand même avoir des répercussions sur la façon dont on gère les objets dans les fonctions. Surtout la portée.
Cette partie est assez technique et assez "optionnelle" dans la mesure où vous n'avez pas besoin de l'utiliser dans les parties suivantes. Par contre, il faudra les lire un jour car sinon vous risquez de ne pas comprendre ce qui cloche dans votre beau code.
Si vous avez du courage, en route. Sinon, dites le moi que je vous fasse un petit résumé.
Si on désigne une variable toto dans une fonction, Python commence par regarder si ce nom toto est présent dans l'espace des noms de la fonction.
Si la réponse est OUI, l'entité toto est un objet local : on ne pourra pas y accéder depuis ailleurs et il sera détruit par le ramasse-miette une fois qu'on sortira de la fonction.
Si la réponse est NON, Python va voir si quelque chose nommée toto existe dans l'espace des noms du corps du programme. On pourra alors y accéder en lecture mais pas en écriture.
Par contre, on peut utiliser le mot-clé GLOBAL pour permettre à la fonction d'agir sur l'entité.
Regardons si tout ceci est vrai avec les objets.
08° Utiliser le code suivant qui crée un personnage perso_1 et perso_2. Il appelle une fonction modifier qui impose perso_1 = perso_2
. On affiche perso_1 dans la fonction et au retour dans le corps du programme.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import mes_classes
# mes classes car le fichier externe se nomme mes_classes.py
# - - - - - - - - - - - - - - - - - -
# Déclarations des fonctions
# - - - - - - - - - - - - - - - - - -
def modifier() :
perso_1 = perso_2
print("\nDans la fonction elle-même, contenu de perso_1 :")
perso_1.presentation()
# - - - - - - - - - - - - - - - - - -
# Corps du programme
# - - - - - - - - - - - - - - - - - -
perso_1 = mes_classes.Personnage()
perso_1.nom = "Skywalker"
perso_1.prenom = "Anakin"
perso_1.niveau = 1
perso_1.classe = "Jedi noir"
perso_2 = mes_classes.Personnage()
modifier()
print("\nAprès le retour dans le corps du programme, contenu de perso_1 :")
perso_1.presentation()
input("\nAppuyer sur ENTREE")
...CORRECTION...
La fonction parvient à créer un objet perso_1 à partir de perso_2.
Mais, c'est un nouvel objet local qui est créé lorsque vous avez utiliser perso_1 = perso_2.
Il faut le lire ainsi : perso_1_de_la_fonction = perso_2_du_programme_principal.
De retour dans le programme principal, on voit que perso_1 n'a pas bougé : lorsqu'on lit perso_1 dans le programme principal, Python va chercher perso_1_du_programme_principal.
Dans la fonction elle-même, contenu de perso_1 :
nobody nobody
aucune classe de niveau 0
Après le retour dans le corps du programme, contenu de perso_1 :
Skywalker Anakin
Jedi noir de niveau 1
Appuyer sur ENTREE
BILAN : effectivement, si on tente en Python de modifier avec un signe EGAL "=" un objet dans une fonction, cela ne fait que créer une version locale qui disparaitra ensuite. Le comportement des variables-objets" et des "variables-typées" est donc identique de ce point de vue.
Mais si on tente juste de modifier une attribut ? Avec un code du type :
perso_1.nom = "Titi"
09° Utiliser le code suivant qui crée un personnage perso_1. Il appelle une fonction modifier qui impose perso_1.nom = "Titi"
. On affiche perso_1 dans la fonction et au retour dans le corps du programme.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import mes_classes
# mes classes car le fichier externe se nomme mes_classes.py
# - - - - - - - - - - - - - - - - - -
# Déclarations des fonctions
# - - - - - - - - - - - - - - - - - -
def modifier() :
perso_1.nom = "Titi"
print("\nDans la fonction elle-même, contenu de perso_1 :")
perso_1.presentation()
# - - - - - - - - - - - - - - - - - -
# Corps du programme
# - - - - - - - - - - - - - - - - - -
perso_1 = mes_classes.Personnage()
perso_1.nom = "Skywalker"
perso_1.prenom = "Anakin"
perso_1.niveau = 1
perso_1.classe = "Jedi noir"
modifier()
print("\nAprès le retour dans le corps du programme, contenu de perso_1 :")
perso_1.presentation()
input("\nAppuyer sur ENTREE")
...CORRECTION...
La fonction parvient à modifier l'attribut de l'objet !
Une fois de retour dans le corps du programme, le nom stocké dans perso_1_programme_principal a bien été modifié.
Pourquoi ? Simplement car on utilse perso_1 du programme principal en lecture dans la fonction : souvenez-vous, perso_1 ne contient qu'une référence à l'objet, pas l'objet lui-même.
Ici, on utilise donc l'adresse contenue dans perso_1 pour aller agir sur l'un des attributs. C'est donc légal.
Dans la fonction elle-même, contenu de perso_1 :
Titi Anakin
aucune classe de niveau 0
Après le retour dans le corps du programme, contenu de perso_1 :
Titi Anakin
Jedi noir de niveau 1
Appuyer sur ENTREE
Il faudra donc faire attention : vous pouvez modifier les valeurs des attributs des objets depuis une fonction car on transmet la référence (l'id en Python ou l'adresse pour d'autres langages).
Ainsi, depuis une fonction, la modification d'attributs ou l'utilisation de méthodes, permet d'agir réellement sur l'objet : perso_1.niveau = 3
va réellement mettre le niveau à 3 dans l'objet du corps du programmme et perso_1.perdre_un_niveau()
va réellement faire diminuer le niveau de l'objet désigné. Il ne s'agira pas d'une action sur un duplicata local.
Pour rappel, voici un exemple de programme passant un integer x en argument : on est sur le cas de la variable 'typée' :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import mes_classes
# mes classes car le fichier externe se nomme mes_classes.py
# - - - - - - - - - - - - - - - - - -
# Déclarations des fonctions
# - - - - - - - - - - - - - - - - - -
def modifier(y) :
y = y/2
print("\nDans la fonction elle-même : y = ", y)
# - - - - - - - - - - - - - - - - - -
# Corps du programme
# - - - - - - - - - - - - - - - - - -
x = 50
modifier(x)
print("\nAprès le retour dans le corps du programme : x = ", x)
input("\nAppuyer sur ENTREE")
Le résultat est que la variable x n'est pas modifié par le fait qu'elle soit transmise à la fonction et que la fonction modifie ensuite localement le paramètre y qui reçoit x : on crée un paramètre y qui contient la même chose que x.
Regardons ce qui se passe pour un objet passé en argument.
10° Utiliser le code suivant pour répondre à la question suivante : un objet passé en argument est-il modifié lors de l'utilisation d'un signe = dans la fonction ?
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import mes_classes
# - - - - - - - - - - - - - - - - - -
# Déclaration des fonctions
# - - - - - - - - - - - - - - - - - -
def modifier(perso_x) :
perso_x = perso_2
print("\nDans la fonction elle-même :")
perso_x.presentation()
# - - - - - - - - - - - - - - - - - -
# Corps du programme
# - - - - - - - - - - - - - - - - - -
perso_1 = mes_classes.Personnage()
perso_1.nom = "Solo"
perso_1.prenom = "Han"
perso_1.niveau = 4
perso_1.classe = "Contrebandier"
perso_2 = mes_classes.Personnage()
modifier(perso_1) print("\nAprès le retour dans le corps du programme :") perso_1.presentation() input("\nAppuyer sur ENTREE")
...CORRECTION...
Initialement, perso_x contient bien la référence au même objet que perso_1. Les deux pointent vers le même objet.
Mais dans la fonction, on crée une variable locale perso_x qu'on remplit avec la référence perso_2 : perso_x = perso_2.
Lorsqu'on execute alors (dans la fonction) perso_x.presentation(), on affiche la présentation contenu de l'objet pointé par perso_2.
De retour dans le programme principal, on voit bien que perso_1 n'a pas été modifié alors qu'il a été fourni en argument pour remplir le paramètre perso_x.
Dans la fonction elle-même :
nobody nobody
aucune classe de niveau 0
Après le retour dans le corps du programme :
Solo Han
Contrebandier de niveau 4
Appuyer sur ENTREE
Regardons ce qui se passe pour les objets passés en argument si on tente de les modifier à l'aide du point (changement d'attributs ou utilisation d'une méthode).
11° Utiliser le code suivant pour répondre à la question suivante : un objet passé en argument est-il modifié lors de l'utilisation d'un point (sur un attribut ou une méthode) dans la fonction ?
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import mes_classes
# mes classes car le fichier externe se nomme mes_classes.py
# - - - - - - - - - - - - - - - - - -
# Déclarations des fonctions
# - - - - - - - - - - - - - - - - - -
def modifier(perso_x) :
perso_x.nom = "Titi"
print("\nDans la fonction elle-même :")
perso_x.presentation()
# - - - - - - - - - - - - - - - - - -
# Corps du programme
# - - - - - - - - - - - - - - - - - -
perso_1 = mes_classes.Personnage()
perso_1.nom = "Solo"
perso_1.prenom = "Han"
perso_1.niveau = 4
perso_1.classe = "Contrebandier"
modifier(perso_1)
print("\nAprès le retour dans le corps du programme :")
perso_1.presentation()
input("\nAppuyer sur ENTREE")
...CORRECTION...
Cette fois, le paramètre perso_x contient tout le temps la référence au même objet que perso_1 fourni en argument.
Lorsqu'on tape perso_x.nom cela pointe donc au même endroit que perso_x_1.nom.
Le contenu vers lequel dirige perso_1 est donc bien modifié.
Dans la fonction elle-même :
Titi Han
Contrebandier de niveau 4
Après le retour dans le corps du programme :
Titi Han
Contrebandier de niveau 4
Appuyer sur ENTREE
Bien entendu, cela fonctionne également avec l'utilsation d'une méthode. Tant qu'on utilise un point plutôt qu'un =, c'est bon.
Voilà, c'est fini. Tentez maintenant de vous souvenir qu'on transmet toujours l'adresse d'un objet lorsqu'on fournit en argument une 'variable-objet'.
Un objet contient des attributs. Pour l'instant, nous les avons utilisé par stocker des types simples (integer, string ...). Mais en réalité, les attributs d'un objet peuvent stocker des variables-objets également.
Imaginons que nous voulions que nos héros de classe Personnage puissent porter une arme et une armure.
Nous allons créer une classe Arme et une classe Armure.
Nous allons y inclure une méthode __init__ pour l'initialiser et une méthode presentation qui en donnera les caractéristiques.
La méthode présentation de Personnage va être modifiée pour intégrer les présentations d'arme et d'armure du personnage.
12° Modifier le fichier mes_classes.py :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import random
# - - - - - - - - - - - - - - - - - -
# Déclarations des classes
# - - - - - - - - - - - - - - - - - -
class Arme :
"Cette classe contient les infos sur une arme"
def __init__(self):
self.nom = 'poings'
self.degats = 1
self.type = '[arme]'
self.distance = 'contact'
def presentation(self):
"Affiche quelques unes des valeurs de l'arme"
print('Arme : ', self.nom, ' qui fait ',self.degats, 'dégâts à la distance :',self.distance)
class Armure :
"Cette classe contient les infos sur une armure"
def __init__(self):
self.nom = 'aucune'
self.protection = 0
self.type = '[armure]'
def presentation(self):
"Affiche quelques unes des valeurs stockées de l'armure"
print('Armure :', self.nom, ' qui protège de ',self.protection, 'dégâts')
class Personnage:
"Ceci est une classe permettant de créer un personnage dans mon super RPG"
def __init__(self) :
self.nom = 'nobody'
self.prenom = 'nobody'
self.niveau = 0
self.classe = 'aucune classe'
self.arme = Arme() # NOUVEAU
self.armure = Armure() # NOUVEAU
def presentation(self) :
"Affiche quelques unes des valeurs stockées dans le personnage"
print(self.nom, ' ',self.prenom)
print(self.classe, ' de niveau ',self.niveau)
self.arme.presentation() # NOUVEAU
self.armure.presentation() # NOUVEAU
def perdre_un_niveau(self) :
"Cette méthode permet de réduire le niveau de 1"
self.niveau += -1
def combat(self,adversaire) :
"Ceci est une méthode qui renvoie le nom du combattant qui perd"
perdant = ''
test = True
while test :
valeur1 = random.randint(1,6)+self.niveau
valeur2 = random.randint(1,6)+adversaire.niveau
if valeur1>valeur2 :
perdant = adversaire
test = False
elif valeur2>valeur1 :
perdant = self
test = False
perdant.perdre_un_niveau()
return(perdant)
13° Créer un fichier Python qui va utiliser l'ensemble. Créons par exemple un pistolet laser et affectons le à Han Solo :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import mes_classes
# mes classes car le fichier externe se nomme mes_classes.py
# - - - - - - - - - - - - - - - - - -
# Corps du programme
# - - - - - - - - - - - - - - - - - -
pistolaser = mes_classes.Arme()
pistolaser.nom = "Pistoler Laser"
pistolaser.degats = 2
pistolaser.distance = 'courte'
perso_1 = mes_classes.Personnage()
perso_1.nom = "Solo"
perso_1.prenom = "Han"
perso_1.niveau = 4
perso_1.classe = "Contrebandier"
perso_1.arme = pistolaser
print("Personnage")
perso_1.presentation()
input("\nAppuyer sur ENTREE")
En lançant l'application, vous devriez commencer à voir la puissance de la programmation Objet : en créant quelques classes, en intégrant des classes dans d'autres classes, on parvient à coder des choses très compliquées en quelques lignes. Regardez la taille de votre programme principal par rapport à ce qu'il fait en réalité, et vous comprendrez ce que je veux dire...
14° Comment se nomme la variable qui fait référence à l'objet Pistolet Laser ?
...CORRECTION...
La variable pistolaser pointe vers l'objet définissant le pistolet laser.
15° Où se trouve l'affection du pistolet Laser à Han Solo ?
...CORRECTION...
Tout se fait sur cette ligne :
perso_1.arme = pistolaser
16° Où se trouve dans Personnage l'instruction d'afficher l'arme ?
...CORRECTION...
self.arme.presentation() # NOUVEAU
On cherche dans l'attribut arme de l'instance de Personnage la référence à l'arme :
self.arme
Une fois trouvée, on utilise la méthode presentation de la classe Arme sur l'instance qu'on vient de trouver juste avant.
self.arme.presentation()
17° Pourquoi ne pas utiliser de print lorsqu'on utilise self.arme.presentation()
?
...CORRECTION...
Le print est dans la méthode presentation de la classe Arme.
18° Modifier le code pour l'armure et l'arme aient un impact lors des combats. Par exemple (si vous êtes en panne d'inspiration) :
19° Rajouter un test pour qu'on ne puisse jamais avoir moins de 0 en armure et en niveau.
20° Créer une méthode combat_total : les deux objets Personnages doivent se battre jusqu'à ce qu'un des deux tombe au niveau 0.
Et bien, vous avez maintenant de nombreux outils à disposition.
Notre jeu de Pac-man serait maintenant assez facile à concevoir puisqu'il "suffit" de coder une classe PacManJoueur et une classe PacManAdversaire. Et de créer les instances nécessaires.
Oui mais ... Les deux classes de PacMan ont certainement énormément de lignes de codes similaires non ?
Oui, et c'est ce que nous allons voir lors de la prochaine activité Objets-3 : comment créer des classes à partir d'autres classes.
Et vous commencerez à comprendre la puissance de la "Programmation Orientée Objet".
En attendant, voici une correction possible des dernières questions :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import random
# - - - - - - - - - - - - - - - - - -
# Déclarations des classes
# - - - - - - - - - - - - - - - - - -
class Arme :
"Cette classe contient les infos sur une arme"
def __init__(self):
self.nom = 'poings'
self.degats = 1
self.type = '[arme]'
self.distance = 'contact'
def presentation(self):
"Affiche quelques unes des valeurs de l'arme"
print('Arme : ', self.nom, ' qui fait ',self.degats, 'dégâts à la distance :',self.distance)
def supprimer_arme(self) : # NOUVEAU
"Annule les données de l'arme en réinitialisant les valeurs grace à __init__"
self.__init__()
class Armure :
"Cette classe contient les infos sur une armure"
def __init__(self):
self.nom = 'aucune'
self.protection = 0
self.type = '[armure]'
def presentation(self):
"Affiche quelques unes des valeurs stockées de l'armure"
print('Armure :', self.nom, ' qui protège de ',self.protection, 'dégâts')
def reduire_armure(self,reduction): # NOUVEAU
"Réduit la protection de l'armure du paramètre fourni dans reduction"
self.protection -= reduction
if self.protection < 0 :
self.__init__()
class Personnage:
"Ceci est une classe permettant de créer un personnage dans mon super RPG"
def __init__(self) :
self.nom = 'nobody'
self.prenom = 'nobody'
self.niveau = 0
self.classe = 'aucune classe'
self.arme = Arme()
self.armure = Armure()
self.de = 0 # NOUVEAU
def presentation(self) :
"Affiche quelques unes des valeurs stockées dans le personnage"
print(self.nom, ' ',self.prenom)
print(self.classe, ' de niveau ',self.niveau)
self.arme.presentation()
self.armure.presentation()
def perdre_un_niveau(self) :
"Cette méthode permet de réduire le niveau de 1"
self.niveau += -1
if self.niveau < 0 : # NOUVEAU
self.niveau = 0 # NOUVEAU
def gestion_arme_armure(self,referenceDefenseur) : # NOUVEAU
"""Cette méthode permet de gérer l'efficacité et les dommages subis par l'arme et l'armure de l'attaquant
Elle renvoie finalement la valeur de l'attaquant pour ce combat
Le paramètre attendu est la référence vers le Personnage en défense sur ce calcul"""
valeurAttaquant = self.de
valeurDefenseur = referenceDefenseur.de
if valeurDefenseur == 6 :
self.armure.reduire_armure(2)
elif valeurDefenseur >= 4 :
self.armure.reduire_armure(1)
if valeurAttaquant == 1 :
self.arme.supprimer_arme()
valeur_combat = valeurAttaquant + self.niveau + self.arme.degats - referenceDefenseur.armure.protection
return(valeur_combat)
def combat_total(self,adversaire) :
"Ceci est une méthode qui fait combattre les adversaires tant qu'aucun n'a atteint le niveau 0"
while not(self.niveau == 0 or adversaire.niveau == 0) :
print("\n",self.combat(adversaire).nom, " vient de perdre un combat")
def combat(self,adversaire) :
"Ceci est une méthode qui renvoie le nom du combattant qui perd"
perdant = ''
test = True
while test :
self.de = random.randint(1,6) # NOUVEAU
adversaire.de = random.randint(1,6) # NOUVEAU
valeur1 = self.gestion_arme_armure(adversaire) # MODIFIE
valeur2 = adversaire.gestion_arme_armure(self) # MODIFIE
if valeur1 > valeur2 :
perdant = adversaire
test = False
elif valeur2 > valeur1 :
perdant = self
test = False
perdant.perdre_un_niveau()
return(perdant)
En voici un exemple d'utilisation : un combat entier géré entre Han Solo et un Stormtrooper :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import mes_classes
# mes classes car le fichier externe se nomme mes_classes.py
# - - - - - - - - - - - - - - - - - -
# Corps du programme
# - - - - - - - - - - - - - - - - - -
pistolaser = mes_classes.Arme()
pistolaser.nom = "Pistoler Laser"
pistolaser.degats = 2
pistolaser.distance = 'courte'
fusilaser = mes_classes.Arme()
fusilaser.nom = "Fusil Laser"
fusilaser.degats = 3
fusilaser.distance = 'longue'
vestecuir = mes_classes.Armure()
vestecuir.nom = "Une simple veste en cuir"
vestecuir.protection = 1
armurestorm = mes_classes.Armure()
armurestorm.nom = "Une belle armure blanche"
armurestorm.protection =4
perso_1 = mes_classes.Personnage()
perso_1.nom = "Solo"
perso_1.prenom = "Han"
perso_1.niveau = 6
perso_1.classe = "Contrebandier"
perso_1.arme = pistolaser
perso_1.armure = vestecuir
perso_2 = mes_classes.Personnage()
perso_2.nom = "Boum"
perso_2.prenom = "Bob"
perso_2.niveau = 2
perso_2.classe = "Stormtrooper"
perso_2.arme = fusilaser
perso_2.armure = armurestorm
print("\n-------------------- Avant le combat :")
print("\nPersonnage")
perso_1.presentation()
print("\nPersonnage")
perso_2.presentation()
print("\n-------------------- Le combat :")
perso_1.combat_total(perso_2)
print("\n-------------------- Après le combat :")
print("\nPersonnage")
perso_1.presentation()
print("\nPersonnage")
perso_2.presentation()
input("\nAppuyer sur ENTREE")