• 6 heures
  • Facile

Ce cours est visible gratuitement en ligne.

Ce cours est en vidéo.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

J'ai tout compris !

Mis à jour le 04/02/2019

Créez un graphique !

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

 

C'est parti pour la création de notre graphique !

Options du graphique de base

Avant tout, commençons par écrire la manière dont nous souhaitons que se lance notre graphique. Dans la fonction main(), ajoutons quelques lignes de pseudo code :  

    # Initialisation du graphique 
    # agreeableness_graph = AgreeablenessGraph()
    
    # Affichage du graphique. On passe en paramètre la liste de nos zones pour y avoir accès à l'intérieur de notre classe AgreeablenessGraph.
    # agreeableness_graph.show(Zone.ZONES) 

Passons désormais à la définition de la classe. 

Créons la classe Graph et ajoutons la méthode __init__(), comme nous l'avons déjà fait plusieurs fois jusqu'à maintenant. A l'intérieur, indiquons les attributs.

Je sais que mes graphiques auront chacun une abscisse, une ordonnée et un titre. Je veux également une grille en fond afin que la granularité du graphique soit plus évidente.

class BaseGraph:

    def __init__(self):
        self.title = "Your graph title"
        self.x_label = "X-axis label"
        self.y_label = "X-axis label"
        self.show_grid = True

Le module MatPlotLib

Matplotlib est une librairie Python utilisée pour représenter des données en deux dimensions. Concrètement, elle vous permet de créer des schémas, des graphiques et même des spectres de couleurs. Elle est assez vaste et nous n'aurons pas besoin de toutes ses fonctionnalités.

En fait, nous allons utiliser un de ses modules qui s'appelle Pyplot et qui permet de créer des graphiques en couleur. Exactement ce dont nous avons besoin !

Commencez par installer le module. Dans votre console, écrivez pip install matplotlib.

Puis importez-le dans votre script en indiquant, tout en haut, import matplotlib.pyplot as plt.

Je vous laisse lire la documentation et réfléchir à la manière dont vous pourrez créer un graphique.

...

Vous êtes prêt·e ? 😎

La création d'un graphique se fait en plusieurs phases :

  • Initialisation du graphique : méthode plot() qui prend en argument les valeurs de l'abscisse, celles de l'ordonnée et le type de graphique voulu.  .plot(x_values, y_values, graph_type)

  • Ajout de la légende de l'abscisse : .xlabel(label)

  • Ajout de la légende de l'ordonnée : .ylabel(label)

  • Ajout du titre : .title()

  • En option, nous pouvons indiquer si nous désirons une grille dans le fond : .grid()

  • Création du graphique et ouverture d'une nouvelle fenêtre : .show()

Nous allons utiliser tout cela dans une nouvelle méthode : .show()

class Graph:
    ...
    def show(self, zones):
        # x_values = gather only x_values from our zones
        # y_values = gather only y_values from our zones
        plt.plot(x_values, y_values, '.')
        plt.xlabel(self.x_label)
        plt.ylabel(self.y_label)
        plt.title(self.title)
        plt.grid(self.show_grid)
        plt.show()

Comprendre l'héritage

Nous avons parlé de la classe Graph qui allait avoir les attributs x (données en abscisse) et y (données en ordonnée). Pourtant, nous n'aurons pas besoin des mêmes données pour nos deux graphiques et, surtout, le calcul sera différent. Comment faire pour que nos graphiques partagent certaines méthodes et en adaptent d'autres ?

Laissez-moi vous présenter, sous un tonnerre d'applaudissements, l'héritage !

L'héritage permet de créer des "sous-classes" qui vont étendre ou modifier les méthodes de la classe "commune".
En programmation, nous parlerons plutôt de "classe parent" pour désigner la classe commune et de "classes enfant" pour désigner les classes qui héritent de la classe parent.

Commençons par la classe parent que nous appellerons BaseGraph. Son but est d'intégrer tous les éléments dont nous aurons besoin, a minima, pour créer un graphique. Quelles méthodes ? Quels attributs ?

Mais nous ne créerons jamais d'instance directement à partir de cette classe, nous utiliserons les classes enfant pour cela. La classe parent existe uniquement pour donner une ligne de conduite, une base. Les classes enfant sont, elles, chargées d'appliquer cette ligne de conduite.

En résumé, la classe parent peut être vue comme une loi et les classes enfant comme les décrets d'application.
Si vous n'êtes pas très sensible à ma métaphore législative, ce n'est pas grave. Reprenons l'exemple du sens de la conduite. Si nous avions à réaliser le programme correspondant, voici ce que nous écririons certainement.

Classe parent : TypeDeConduite

sens_de_la_conduite  : attribut "droite" ou "gauche"
depasser() : méthode pour faire avancer la voiture
klaxonner() : méthode pour savoir dans quel cas il faut klaxonner. 

Nous savons que, peu importe le pays, il y aura toujours, a minima, un sens de la conduite et que chacun devra dépasser ou klaxonner un jour. Pour autant, nous ne voulons pas encore dire comment. Ce sera à chaque classe enfant de définir les règles applicables dans le pays.

Classes enfant : ConduiteAnglaise et ConduiteFrancaise

Nous indiquons alors les éléments demandés. Si nous oublions de le faire, le programme nous renverra une erreur nous indiquant que nous avons oublié un élément obligatoire. Si nous avons à ajouter des règles pour la France, la priorité à droite par exemple, nous pouvons le faire dans la classe  ConduiteFrancaise.

L'héritage dans notre programme

Maintenant réfléchissons un peu. Notre classe parent sera  BaseGraph. Elle aura deux classes enfant qui correspondent aux deux types de graphique que nous voulons créer :  AgreeablenessGraph et IncomeGraph

Quels vont être les éléments qui vont vraiment varier dans ces classes enfant ? Les attributs (le titre et les légendes) ainsi que les valeurs. Nous allons donc utiliser, dans les classes enfant, la méthode __init__ et nous devrons trouver le moyen de faire varier les données.

Renommons la classe parent (BaseGraph) puis créons la première classe enfant. Nous allons dessiner le graphique qui met en relation le taux d'agréabilité et la densité de population :  AgreeablenessGraph

Hériter d'une classe en Python est très simple : vous l'indiquez entre parenthèses lorsque vous créez la classe enfant. Comme ceci :

class AgreeablenessGraph(BaseGraph): 
    pass

Ajoutons une méthode  __init__()  et les attributs que nous souhaitons changer :

class AgreeablenessGraph(BaseGraph):    
    def __init__(self):
        self.title = "Nice people live in the countryside"
        self.x_label = "population density"
        self.y_label = "agreeableness"

Parfait. De cette façon, nous remplaçons la méthode d'origine et indiquons au programme d'utiliser nos valeurs et pas celles de la classe mère.

Une classe enfant peut utiliser une méthode héritée de la classe parent comme s'il s'agissait de la sienne. Elle peut néanmoins également la redéfinir entièrement si cela est nécessaire. 

Je ne sais pas si vous vous en rendez compte, mais il manque un élément. Vous l'avez trouvé ? Oui, il s'agit bien de la grille !

Parlons-en, justement, de la grille. Nous l'aurons à la fois pour ce graphique et pour le suivant. C'est un prérequis. Je devrais donc être en mesure de l'indiquer dans ma classe parent et de l'importer, automatiquement, dans mes classes enfant.

Vous allez utiliser pour cela la méthode super() et la méthode parent que vous voulez utiliser. Dans notre cas :

class AgreeablenessGraph(BaseGraph):

    def __init__(self):
        super().__init__() # executes the parent's __init__() method
        self.title = "Nice people live in the countryside"
        self.x_label = "population density"
        self.y_label = "agreeableness"

Intégrer des données

Pour rappel, voici où en est notre code :

 class BaseGraph:

    def __init__(self):
        self.title = "Your graph title"
        self.x_label = "X-axis label"
        self.y_label = "X-axis label"
        self.show_grid = True

    def show(self, zones):
        # x_values = gather only x_values from our zones
        # y_values = gather only y_values from our zones
        plt.plot(x_values, y_values, '.')
        plt.xlabel(self.x_label)
        plt.ylabel(self.y_label)
        plt.title(self.title)
        plt.grid(self.show_grid)
        plt.show()


class AgreeablenessGraph(BaseGraph):

    def __init__(self):
        super().__init__()
        self.title = "Nice people live in the countryside"
        self.x_label = "population density"
        self.y_label = "agreeableness"

Bien bien bien ! A présent, comment faire varier les données ? Plutôt que de modifier la méthode
 show(), nous allons créer une nouvelle méthode xy_values() dans la classe AgreeablenessGraph  dont le but sera de calculer ces valeurs. J'utilise les list comprehensions que vous connaissez déjà. ;-)

def AgreeablenessGraph(BaseGraph):
    ...
    def xy_values(self, zones):
        x_values = [zone.population_density() for zone in zones]
        y_values = [zone.average_agreeableness() for zone in zones]
        return x_values, y_values

J'ai accès aux zones puisque je les ai passées en paramètre en lançant ma fonction. Rappelez-vous :

    agreeableness_graph.show(Zone.ZONES)

Mais... Ah mais en fait non. Je ne devrais pas mettre cette méthode dans cette classe mais plutôt dans la classe parent. Je pourrai ainsi l'appeler depuis la méthodeshow(). Ce serait malin non ?!

Je déplace :

class BaseGraph:

    ...

    def xy_values(self, zones):
        x_values = [zone.population_density() for zone in zones]
        y_values = [zone.average_agreeableness() for zone in zones]
        return x_values, y_values

Sauf que... Vous vous en doutez, je ne calculerai pas les données de la même manière en fonction des graphiques que je souhaite générer. Je vais donc créer la même méthode mais dans la classe enfant :

class AgreeablenessGraph(BaseGraph):

    ...

    def xy_values(self, zones):
        x_values = [zone.population_density() for zone in zones]
        y_values = [zone.average_agreeableness() for zone in zones]
        return x_values, y_values

Enfin, j'aimerais qu'une erreur apparaisse si j'oublie d'implémenter cette méthode dans ma classe enfant. En effet, tous nos graphiques auront forcément une abscisse et une ordonnée.

Je vais donc, dans la classe parent, ajouter une erreur :

class BaseGraph:
    ...
    def xy_values(self, zones):
        raise NotImplementedError

Quand vous ne pouvez pas utiliser une classe parent pour vraiment créer une instance, nous disons qu'il s'agit d'une classe abstraite. Pourquoi ? Tout simplement car son objectif n'est pas de créer un objet mais de donner les règles communes, abstraites, qui seront partagées par tous ses enfants. Les classes enfant, qui partagent toutes les mêmes règles de la classe parent, sont appelées des classes concrètes car elles concrétisent des notions plus abstraites.

Lançons le programme... et tout fonctionne ! Youpi !

L'héritage multiple

Python permet à une classe enfant d'hériter de plusieurs classes parent (comme dans une famille recomposée !). Cela s'appelle l'héritage multiple ou Multiple Inheritance.

Par exemple, une classeVampire pourrait hériter de la classeHumain et de la classe Hématophage (et dePersonnageNiaissi nous sommes dans Twilight :-° ). 

Tout comme dans les familles recomposées, cette fonctionnalité est sujette à controverse et ouvre la porte à quelques problématiques. C'est pourquoi je n'entre pas dans le détail de cette fonctionnalité. 

Challenge

Réalisez le challenge de ce chapitre en cliquant sur ce lien. 

 

Code de ce chapitre

Retrouvez le code de ce chapitre ici : https://github.com/OpenClassrooms-Student-Center/la_poo_avec_python/tree/08_inheritance 

 

Exemple de certificat de réussite
Exemple de certificat de réussite