Projet : Amélioration des objets dés

Prérequis :

Nous allons améliorer la classé que nous avions crée lors du projet précédent (en prérequis).

Valeur par défaut

Faites en sorte que l'on puisse créer des dés avec une valeur initiale, On doit pouvoir :

  • créer un dé sans paramètre, Dé(), et sa valeur doit être sur la valeur 1
  • créer un dé avec un paramètre qi sera sa valeur par défaut Dé(4) par exemple.

corrigé

Fichier dé.py :

class Dé:
    def __init__(self, valeur=1):
        self.valeur = valeur

Ajoutez des tests pour prendre en compte de cette nouvelle fonctionnalité.

corrigé

Fichier test_dé.py :

def test_valeur():
    assert Dé().valeur == 1
    assert Dé(valeur=4).valeur == 4

On utilise toujours la technique qui consiste à utiliser directement un objet créé via le constructeur.

Afficher des dés

Remplacez la méthode Dé.texte() par la méthode Dé.__str__().

corrigé

Fichier dé.py :

class dé:

    # ..

    def __str__(self):
        if self.valeur == 1:
            return "⚀"
        elif self.valeur == 2:
            return "⚁"
        elif self.valeur == 3:
            return "⚂"
        elif self.valeur == 4:
            return "⚃"
        elif self.valeur == 5:
            return "⚄"
        else:
            return "⚅"

On utilise toujours la technique qui consiste à utiliser directement un objet créé via le constructeur.

Modifiez les tests pour prendre en compte de cette nouvelle méthode.

corrigé

Fichier test_dé.py :

def test_str():
    dé = Dé()
    assert str(dé) == "⚀"
    dé.valeur = 4
    assert str(dé) == "⚃"

Implémentez la méthode spéciale __repr__ et restez là en utilisant la fonction repr.

corrigé

Fichier dé.py :

class dé:

    # ..

    def __repr__(self):
        return f"Dé(valeur={self.valeur})"

Fichier test_dé.py :

def test_repr():
    assert repr(Dé()) == "Dé(valeur=1)"

Accesseur pour la valeur

On ne devrait pas pouvoir modifier la valeur d'un dé sans le lancer :

Implémentez un accesseur pour l'attribut valeur

corrigé

Il y a plusieurs façon de faire. Nous allons montrer une version concise :

Fichier dé.py :

class Dé:
    def __init__(self, valeur=1):
        self._valeur = valeur

    valeur = property(lambda self: self._valeur)

    def lancer(self):
        self._valeur = random.randrange(1, 6 + 1)

    # ...

On utilise ici deux choses :

  • premièrement le fait que si l'on ne donne qu'un seul argument à la fonction property on ne donne qu'un accesseur et le mutateur est interdit,
  • deuxièmement l'accesseur est décrit sous la forme d'une fonction lambda de python très utiles pour ce genre de choses.

Il faut aussi changer le test de str. On s'autorise à utiliser directement l'attribut _valeur car les tests font partie du code :

On pourrait utiliser l'attribut privé directement:

# ...

def test_str():
    dé = Dé()
    assert str(dé) == "⚀"
    dé._valeur = 4
    assert str(dé) == "⚃"


# ...

Mais ici on préfère utiliser un constructeur (fichier test_dé.py):

# ...

def test_str():
    assert str(Dé()) == "⚀"
    assert str(Dé(4)) == "⚃"


# ...

Le test est plus clair et n'expose pas inutilement les tripes de la classe. C'est la solution à privilégier lorsque c'est possible.

Ajoutez un test pour confirmer que l'on ne peut pas modifier l'attribut valeur

corrigé

L'erreur produite lorsque l'on tente d'affecter l'attribut valeur est un AttributeError (il suffit de tester dans un interpréteur pour voir l'erreur), on ajoute donc le test suivant en n'oubliant pas également d'importer le module pytest :

import pytest

# ... 

def test_valeur_sans_setter():

    with pytest.raises(AttributeError):
        Dé().valeur = 1    

min et max dans classe

Notre classe Dé contient 2 magic numbers qui sont les bornes du lancé. Supprimons-les :

Créez deux attributs de classes MIN_VALEUR et MAX_VALEUR qui vont permettre de supprimer les magics numbers de la méthode Dé.lancer()

corrigé

Fichier dé.py :

class dé:
    MIN_VALEUR = 1
    MAX_VALEUR = 6

    # ...

    def lancer(self):
        self._valeur = random.randrange(self.MIN_VALEUR, self.MAX_VALEUR + 1)

Comparaisons

Créez et testez la comparaison < entree deux dés (méthode spéciale __lt__).

corrigé

Fichier dé.py :

class dé:

    # ...

    def __lt__(self, other):
        return self._valeur < other.valeur

Fichier test_dé.py :

def test_lt():
    d1 = Dé()
    d2 = Dé()
    assert not d1 < d2

    d2._valeur = 5
    assert d1 < d2
    assert not d2 < d1

Cette comparaison permet de trier des listes de dés !

Créez le fichier main.py et placez y le code suivant que vous pourrez tester :

from dé import Dé

liste_d = [Dé() for _ in range(5)]

print(liste_d)

for d in liste_d:
    d.lancer()

print(liste_d)
liste_d.sort()
print(liste_d)

Code final