Exo Objets 1

Identification

Infoforall

25 - Exos Objets Att


Toutes les questions sont corrigées. Mais écrivez d'abord VOTRE correction. Le jour du BAC, il n'y aura pas la correction.

1 - Création

01° Vocabulaire : observer le programme suivant puis répondre aux questions de type QCM.

1 2 3 4 5 6 7 8 9
import random as rd class De : def __init__(self, faces): self.type = faces self.valeur = faces self.relance = False
  1. Quel est le type de De ?
    1. un constructeur
    2. une méthode
    3. un attribut
    4. une classe
  2. Comment se nomme __init__ ?
    1. un constructeur
    2. une méthode
    3. un attribut
    4. une classe
  3. Comment se nomme valeur ?
    1. un constructeur
    2. une méthode
    3. un attribut
    4. une classe

...CORRECTION...

  1. Quel est le type de De ?
    1. une classe
  2. Comment se nomme __init__ ?
    1. une méthode
  3. Comment se nomme valeur ?
    1. un attribut

02° Quelqu'un appelle __init__() la méthode-constructeur. A-t-il totalement raison, totalement tord ou est-ce un abus de langage courant ?

...CORRECTION...

C'est un abus de langage mais c'est courant car il n'y a que le constructeur De() qui fait appel à cette méthode.

03° Quelqu'un lance ceci dans la console :

>>> de1 = De(6)

Lors de cet appel :

  1. Que va contenir le paramètre self de la méthode __init__() ?
  2. Que va contenir le paramètre faces ?

...CORRECTION...

  1. Le paramètre self va contenir l'adresse ou la référence du nouvel objet que le contructeur De(6) aura créé juste à l'étape précédente de la création.
  2. Le paramètre faces va contenir 6 car le constructeur fournit à __init__() l'ensemble des arguments qu'il a lui même reçu.

04° Que va provoquer cette instruction ? Le renvoi d'une valeur ou une erreur. Justifiez la réponse.

>>> de1.faces

...CORRECTION...

      1 2 3 4 5 6 7 8 9
      import random as rd class De : def __init__(self, faces): self.type = faces self.valeur = faces self.relance = False
    1. de1 est bien un objet en mémoire pour le moment.
    2. Il possède ne possède pas d'attribut nommé faces.
    3. On obitent donc une erreur.

    faces est le nom du paramètre envoyé à __init__(), pas le nom d'un attribut.

05° Que vont contenir les attributs valeur et relance de l'objet de1 ?

>>> de1 = De(6)

...CORRECTION...

Le 6 envoyé va se stocker dans le deuxième paramètre de la méthode __init__() puisque self est géré automatiquement par Python.

Il faut donc voir la méthode spéciale __init__().

1 2 3 4 5 6 7 8 9
import random as rd class De : def __init__(self, faces): self.type = faces self.valeur = faces self.relance = False

Le 6 est donc reçu dans faces.

On voit que l'attribut valeur va référencer 6 et que l'attribut relance référence False.

06° Que vont contenir les attributs type, valeur et relance de l'objet de2 ?

>>> de2 = De(20)

...CORRECTION...

L'attribut type référence 20.

L'attribut valeur référence 20.

L'attribut relance référence False.

07° Quel autre nom qu'objet pourrait-on donner aux objets de1 et de2  ?

  1. création de la classe
  2. instance de la classe
  3. réalisation de la classe
  4. attribution de la classe

...CORRECTION...

  1. instance de la classe De

2 - Importation

On rappelle que la fonction randint() du module random permet d'obtenir un entier aléatoirement sélectionné entre une valeur minimale et une valeur maximale (inclusesà fournies en argument.

randint(1, 8) va renvoyer un entier choisi au hasard parmi 1, 2, 3, 4, 5, 6, 7 et 8.

08° Quelle ligne, parmi 3-4-5-6, permet d'utiliser correctement cette fonction au vu de l'importation de la ligne 1 ?

1 2 3 4 5 6
import random as rd a = random.randint(1, 8) b = rd.randint(1, 8) c = randint(1, 8) d = randint(1, 8).random

...CORRECTION...

On accède à ce qui se trouve derrière le import. Ici, c'est donc rd à cause de l'alias.

b = rd.randint(1, 8)

09° Comme quel type d'entité informatique semble se comporter un module dans Python ?

Indice : c'est un peu dans le titre de l'activité...

...CORRECTION...

On retrouve la syntaxe typique de la programmation objet : objet.attribut.

3 - Réalisation du dé

10° Modifier le code de la méthode initialisateur __init__() pour que chaque nouveau dé provoque le tirage d'un nombre aléatoire entre 1 et le nombre de faces. On stockera bien entendu cette valeur dans l'attribut nommée valeur et il faudra utiliser le module random, et donc l'importer.

Ainsi, si on crée un dé à 6 faces, on veut obtenir une valeur aléatoire comprise entre 1 et 6. La valeur doit bien entendu être la même tant qu'on a pas relancé ce dé.

Quelques exemples d'utilisation :

>>> de1 = De(6) >>> de1.valeur 3 >>> de2 = De(20) >>> de2.valeur 12 >>> de1.valeur 3

On remarquera bien que le dé 1 n'a pas été modifié après création du dé 2 : il s'agit bien d'entités indépendantes, ayant chacune sa propre adresse.

...CORRECTION...

1 2 3 4 5 6 7 8 9
import random as rd class De: def __init__(self, faces): self.type = faces self.valeur = rd.randint(1, faces) self.relance = False

11° Réaliser maintenant une variante : on veut créer des dés explosifs à travers une classe qu'on nommera DeExplosif : cela veut dire qu'avec un dé à 6 faces, le fait de tirer 6 augmente à nouveau sa valeur d'une valeur comprise entre 1 et 6.

Ainsi, un 5 donnerait toujours 5.

Par contre, si on tire initialement un 6, le résultat final dans le dé sera 6 + une valeur entre 1 et 6 (entre 7 et 12 donc).

Exemple d'utilisation :

>>> de1 = DeExplosif(6) >>> de1.valeur 8

Sur l'exemple, on a clairement tiré 6 puis 2. Ce qui donne 8.

On ne considèrera qu'une seule explosion. Sur un nouveau 6, il ne se passera rien de particulier. 6 et 6 donne juste 12.

...CORRECTION...

1 2 3 4 5 6 7 8 9 10 11
import random as rd class DeExplosif: def __init__(self, faces): self.type = faces self.valeur = rd.randint(1, faces) if self.valeur == faces: self.valeur = self.valeur + rd.randint(1, faces) self.relance = False

12° Réaliser maintenant une variante : on veut créer des dés pipés à travers une classe qu'on nommera DeTriche : si le dé donne un résultat strictement inférieur à la moyenne, on veut relancer le résultat.

Le résultat final sera alors le plus grand des deux tirages.

Exemple : un D6 donne 2 initialement. C'est moins que la moyenne ( (6+1)/2 = 3.5 ). On relance et on obtient maintenant 1. Pas de bol. On stocke 2 qui est plus grand que la relance.

Exemple : un D6 donne 3 initialement. C'est moins que la moyenne ( (6+1)/2 = 3.5 ). On relance et on obtient maintenant 5. On stocke 5 puisque la relance est plus grande que la valeur 3 initiale.

...CORRECTION...

1 2 3 4 5 6 7 8 9 10 11
import random as rd class DeTriche: def __init__(self, faces): self.type = faces self.valeur = rd.randint(1, faces) if self.valeur < (1 + faces) / 2: self.valeur = max(self.valeur , rd.randint(1, faces)) self.relance = False

4 - Paramètres nommés par défaut

Un élève produit cette classe :

1 2 3 4 5 6 7 8 9 10 11 12
import random as rd class De : def __init__(self, faces=6, explosif=False): self.type = faces self.explosif = explosif self.valeur = rd.randint(1, faces) if self.explosif and self.valeur == faces : self.valeur = self.valeur + rd.randint(1, faces) self.relance = False

13° Expliquer pourquoi on peut désormais taper ceci :

>>> de1 = De() >>> de1.valeur 4

...CORRECTION...

Tous les paramètres ont des valeurs par défaut.

faces vaut 6 par défaut et explosif est False par défaut.

14° Indiquer ce qu'il faut taper pour créer un dé à 6 faces explosif en utilisant cette classe.

...CORRECTION...

>>> de1 = De(explosif=True)

Ou

>>> de1 = De(faces=6, explosif=True)

Ou

>>> de1 = De(explosif=True, faces=6)

Ou

>>> de1 = De(6, True)

15° Indiquer ce qu'il faut taper pour créer un dé à 20 faces explosif en utilisant cette classe.

...CORRECTION...

>>> de1 = De(faces=20, explosif=True)

Ou

>>> de1 = De(explosif=True, faces=6)

Ou

>>> de1 = De(20, True)

5 - Poignée

On désire maintenant tirer directement une poignée de 5 dés. Par exemple pour jouer au Yahtzee.

Nous n'allons pas créer le jeu en entier (ce sera l'objet du prochain DM). Aucun besoin de connaître les règles ici mais vous les trouverez ici si vous êtes curieux : Yahtzee sur Wikipedia.

Qu'est-ce qu'une poignée de 5 dés ? Une sorte de conteneur contenant 5 instances de la classe De, non ?

Et comme pendant un tel jeu, les joueurs auront plusieurs poignées de dés à réaliser, et bien autant créer une classe Poignee !

16° Observer les lignes 16 et 18. Un étudiant dit qu'il y a une erreur car l'attribut joueur doit impérativement se nommer comme le paramètre player reçu. Expliquer pourquoi il se trompe.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
import random as rd class De : def __init__(self, faces=6, explosif=False): self.type = faces self.explosif = explosif self.relance = False self.valeur = rd.randint(1, faces) if self.explosif and self.valeur == faces : self.valeur = self.valeur + rd.randint(1, faces) class Poignee : def __init__(self, player): self.joueur = player self.points = 0 self.des = [] for _ in range(5): self.des.append( De() )

...CORRECTION...

Aucune obligation à nommer de la même façon le paramètre et l'attribut qui va stocker le contenu de ce paramètre.

Le code de cette question est donc valide MAIS ce n'est pas une bonne pratique de programmation car cela nécessite une lecture attentive du code pour comprendre ce qu'on place où. La plupart du temps, on donne le même nom au paramètre et à l'attribut.

17° QCM : comment se nomme la création du tableau référencé par l'attribut des utilisant la méthode append ?

20 21 22
self.des = [] for _ in range(5): self.des.append( De() )
  • A : Déclaration
  • B : Compréhension
  • C : Extension et rajouts successifs
  • D : Tabloïsation

...CORRECTION...

L'attribut self.des fait référence à un tableau dynamique Python (le type list-Python, celui qui n'est pas une liste chaînée).

On crée d'abord un tableau vide puis on rajoute du contenu au fur et à mesure. Il s'agit donc d'une création par extension et rajouts successifs.

18° QCM : que contiennent les cases du tableau des ?

14 15 16 17 18 19 20 21 22
class Poignee : def __init__(self, player): self.joueur = player self.points = 0 self.des = [] for _ in range(5): self.des.append( De() )
  • A : des Classes De
  • B : des objets ou instances de la classe De
  • C : les valeurs des dés
  • D : de vrais dés, en couleur en plus !

...CORRECTION...

La ligne 22 est explicite : on utilise le constructeur De() et on stocke donc dans la case qu'on rajoute une instance de la classe De.

La réponse est donc B.

19° On veut obtenir les valeurs de dés contenus dans la poignée du joueur. Expliquer comment fonctionne cela en donnant des explications liées au code suivant :

>>> p1 = Poignee('Sauron') >>> p1.joueur 'Sauron' >>> p1.des[0] <__main__.De object at 0x7f3a70faa438> >>> p1.des[0].valeur 6

...CORRECTION...

>>> p1 = Poignee('Sauron') >>> p1.joueur 'Sauron'

On a donc créé une poignée en l'attribuant au joueur nommé Sauron.

>>> p1.des[0] <__main__.De object at 0x7f3a70faa438>

L'attribut des est un tableau et on accède à sa case d'indice 0. Elle contient bien une instance de la classe De.

>>> p1.des[0].valeur 6

Puisque p1.des[0] est un objet de la classe De, il possède un attribut valeur qui contient la valeur du dé.

Si on résume :

  • p1 référence une instance de Poignee
  • p1.des référence un tableau dynamique de type list
  • p1.des[0] référence l'un des éléments du tableau : une instance de De
  • p1.des[0].valeur référence la valeur d'une instance de De

20° Modifier maintenant l'initialisateur. Après tirage des Des de la Poignee, on désire recalculer les points en additionnant les résultats de dés.

Vérifiez votre classe avec quelque chose comme ceci :

>>> p1 = Poignee('Saroumane') >>> p1.points 21 >>> p1.des[0].valeur 5 >>> p1.des[1].valeur 6 >>> p1.des[2].valeur 2 >>> p1.des[3].valeur 5 >>> p1.des[4].valeur 3

...CORRECTION...

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
import random as rd class De : def __init__(self, faces=6, explosif=False): self.type = faces self.explosif = explosif self.relance = False self.valeur = rd.randint(1, faces) if self.explosif and self.valeur == faces : self.valeur = self.valeur + rd.randint(1, faces) class Poignee : def __init__(self, player): self.joueur = player self.points = 0 self.des = [] for _ in range(5): self.des.append( De() ) for i in range(len(self.des)): self.points = self.points + self.des[i].valeur

Dans le prochain DM, nous traiterons des méthodes. Rajouter des méthodes permettrait en effet de finaliser réellement notre jeu de dés.

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