{"cells": [{"cell_type": "markdown", "metadata": {}, "source": ["# 1A.2 - Classes, m\u00e9thodes, attributs, op\u00e9rateurs et carr\u00e9 magique\n", "\n", "Les classes proposent une fa\u00e7on diff\u00e9rente de structurer un programme informatique. Pas indispensable mais souvent \u00e9l\u00e9gant."]}, {"cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [{"data": {"text/html": ["
run previous cell, wait for 2 seconds
\n", ""], "text/plain": [""]}, "execution_count": 2, "metadata": {}, "output_type": "execute_result"}], "source": ["from jyquickhelper import add_notebook_menu\n", "add_notebook_menu()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["La plupart du temps, les classes ne sont pas indispensables et l'exp\u00e9rience montre que la plupart des \u00e9l\u00e8ves choisissent de s'en dispenser lors de la r\u00e9alisation de leur projet. Pourquoi les aborder ? Pour plusieurs raisons :\n", "\n", "- Un langage de programmation dispose des types standards : entier, r\u00e9el, cha\u00eenes de caract\u00e8res, tableaux. Hors de cette liste, il n'y a rien \u00e0 moins de pouvoir le cr\u00e9er, c'est ce que permettent les classes.\n", "- Elles rendent le programme plus lisible : tous les projets cons\u00e9quents utilisent les classes. C'est un peu comme si on disposait d'un vocabulaire enrichi pour d\u00e9crire un programme.\n", "\n", "Prenons l'exemple d'un jeu de cartes. Une _carte_ d\u00e9signe l'objet physique : sa couleur, son num\u00e9ro (ou figure), son atout... Dans un jeu, chaque carte vaut un certain nombre de points, elle est plus ou moins forte qu'une autre... Comment d\u00e9crire une carte de tarot ?\n", "\n", "| option 1 | option 2 | option 3 |\n", "|-------------------------|-----------------------------------------|-----------------------------------------|\n", "| une couleur | une couleur | une couleur |\n", "| un num\u00e9ro | un num\u00e9ro (atout = nombre > 100) | un num\u00e9ro (atout = nombre > 100) |\n", "| un atout | | un nombre de points |\n", "\n", "D\u00e9finir une classe dans votre programme vous permet de d\u00e9finir pr\u00e9cis\u00e9ment ce que le concept signifie selon trois aspects :\n", "\n", "- les **attributs** : les donn\u00e9es que la classe consid\u00e8re comme un tout,\n", "- les **m\u00e9thodes** : des fonctions op\u00e9rant sur les attributs,\n", "- les **op\u00e9rateurs** : des fonctions sp\u00e9cifiques pour d\u00e9finir ce qu'est une addition, une soustraction..."]}, {"cell_type": "markdown", "metadata": {}, "source": ["### Apart\u00e9 sur les classes\n", "\n", "On ne voit pas toujours l'int\u00e9r\u00eat d'utiliser des classes pour un exemple peut-\u00eatre trop simple que celui-ci qui suit. Imaginons une fonction d'optimisation avec 10 param\u00e8tres comme celle-ci [scipy.optimize.minimize](http://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html#scipy.optimize.minimize) o\u00f9 les param\u00e8tres sont transmis sous forme de dictionnaire. Pourquoi faire \u00e7a ? Souvent parce que les param\u00e8tres doivent \u00eatre transmis \u00e0 de nombreuses autres fonctions et qu'il serait fastidieux de r\u00e9p\u00e9ter \u00e0 chaque fois la liste des param\u00e8tres. Au lieu d'utiliser un dictionnaire, on peut \u00e9galement cr\u00e9er une classe qui regrouperait des param\u00e8tres :"]}, {"cell_type": "code", "execution_count": 2, "metadata": {"collapsed": true}, "outputs": [], "source": ["class MesParametres:\n", " def __init__(self):\n", " self.pas_gradient = 0.001\n", " self.constante = 2.0\n", " self.iteration = 1000\n", " # ..."]}, {"cell_type": "markdown", "metadata": {}, "source": ["Ensuite, il suffit de cr\u00e9er une instance cette classe :"]}, {"cell_type": "code", "execution_count": 3, "metadata": {"collapsed": true}, "outputs": [], "source": ["p = MesParametres()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Et maintenant, il suffit d'\u00e9crire une fonction utilisant les param\u00e8tres :"]}, {"cell_type": "code", "execution_count": 4, "metadata": {"collapsed": true}, "outputs": [], "source": ["def fonction_any(f, x, p):\n", " for i in range(0,p.iteration):\n", " # ...\n", " pass"]}, {"cell_type": "markdown", "metadata": {}, "source": ["A quoi sert le param\u00e8tre ``self``, une raisons est qu'on peut cr\u00e9er plein d'instances de param\u00e8tres :"]}, {"cell_type": "code", "execution_count": 5, "metadata": {"collapsed": true}, "outputs": [], "source": ["p1 = MesParametres()\n", "p2 = MesParametres()\n", "p3 = MesParametres()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Cela veut dire plusieurs variables ``p1``, ``p2``, ``p3`` mais une seule fa\u00e7on de les d\u00e9finir toutes, une fa\u00e7on de dire que le code ci-dessus est \u00e0 la fois valide pour ``p1``, ``p2`` et ``p3``. On a besoin d'une sorte de pronom."]}, {"cell_type": "markdown", "metadata": {}, "source": ["### Classes, attributs, op\u00e9rateurs\n", "\n", "On veut d\u00e9finir une classe ``Point`` \u00e9quivalent \u00e0 un point dans le plan."]}, {"cell_type": "code", "execution_count": 6, "metadata": {"collapsed": true}, "outputs": [], "source": ["class Point :\n", " def __init__ (self, x,y) :\n", " self.x = x\n", " self.y = y"]}, {"cell_type": "markdown", "metadata": {}, "source": ["De cette fa\u00e7on, on peut d\u00e9finir un point de coordonn\u00e9es (4,5) :"]}, {"cell_type": "code", "execution_count": 7, "metadata": {"collapsed": true}, "outputs": [], "source": ["p = Point (4,5)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["**Vocabulaire :**\n", "\n", "- ``p`` est une instance de la classe ``Point``. Il n'existe qu'une classe ``Point`` mais autant d'instances qu'on veut. Dans notre cas : instance = variable de type ``Point``.\n", "- ``__init__`` : est un constructeur. Il d\u00e9finit ce que Python doit faire lorsqu'on cr\u00e9e une instance.\n", "- ``self.x``, ``self.y`` sont des attributs (ou des variables \u00e0 l'int\u00e9rieur d'une classe).\n", "\n", "La variable ``self`` peut \u00eatre remplac\u00e9e par n'importe quoi. C'est une convention de langage pour d\u00e9signer l'instance manipul\u00e9e \u00e0 l'int\u00e9rieur de la classe :"]}, {"cell_type": "code", "execution_count": 8, "metadata": {"collapsed": true}, "outputs": [], "source": ["class Point :\n", " def __init__ (moi, x,y) :\n", " moi.x = x\n", " moi.y = y\n", "\n", "p1 = Point(4,5) # moi d\u00e9signe p1 ici\n", "p2 = Point(6,7) # moi d\u00e9signe p2 ici"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Si on utilise ``print`` : "]}, {"cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["<__main__.Point object at 0x00000000072AA208>\n"]}], "source": ["print (p1)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Pour \u00e9viter cela, on peut afficher directement ``x`` et ``y`` :"]}, {"cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["4 5\n"]}], "source": ["print (p1.x, p1.y)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Ou ajouter l'op\u00e9rateur ``__str__`` :"]}, {"cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["point: (4,5)\n"]}], "source": ["class Point :\n", " def __init__ (moi, x,y) :\n", " moi.x = x\n", " moi.y = y\n", " def __str__(moi):\n", " return \"point: ({0},{1})\".format(moi.x, moi.y)\n", " \n", "p = Point(4,5)\n", "print (p)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["On peut \u00e9galement d\u00e9finir l'addition :"]}, {"cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["8,10\n"]}], "source": ["class Point :\n", " def __init__ (moi, x,y) :\n", " moi.x = x\n", " moi.y = y\n", " def __str__(moi):\n", " return \"{0},{1}\".format(moi.x, moi.y)\n", " def __add__(moi, autre_point) :\n", " return Point(moi.x + autre_point.x, moi.y + autre_point.y) \n", " \n", "p = Point(4,5)\n", "print (p + p) "]}, {"cell_type": "markdown", "metadata": {}, "source": ["On peut red\u00e9finir tous les [op\u00e9rateurs num\u00e9riques](https://docs.python.org/3.4/reference/datamodel.html#emulating-numeric-types) mais il en existe beaucoup d'autres comme l'op\u00e9rateur ``[]`` (voir [container](https://docs.python.org/3.4/reference/datamodel.html#emulating-container-types)."]}, {"cell_type": "markdown", "metadata": {}, "source": ["### Exercice 1 : carr\u00e9 magique\n", "\n", "On souhaite appliqer ce qu'on vient de voir pour d\u00e9finir un carr\u00e9 magique qui contient neuf chiffres rang\u00e9s dans un tableau \u00e0 deux dimensions. On ajoutera l'op\u00e9rateur ``__str__``."]}, {"cell_type": "code", "execution_count": 13, "metadata": {"collapsed": true}, "outputs": [], "source": ["class CarreMagique :\n", " def __init__(self, ...) :\n", " # ......\n", " def __str__(self) :\n", " # ......\n", " def __add__(self) :\n", " # ......"]}, {"cell_type": "markdown", "metadata": {}, "source": ["### M\u00e9thodes\n", "\n", "Une m\u00e9thode est une fonction rattach\u00e9e \u00e0 une classe et qui s'applique aux donn\u00e9es de la classe et celles envoy\u00e9es en param\u00e8tres :"]}, {"cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["6.4031242374328485\n"]}], "source": ["class Point :\n", " def __init__ (moi, x,y) :\n", " moi.x = x\n", " moi.y = y\n", " def norm(moi) :\n", " return (moi.x**2 + moi.y**2) ** 0.5\n", " \n", "p = Point(4,5)\n", "print (p.norm())"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Avec un param\u00e8tre :"]}, {"cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["9.0\n", "6.4031242374328485\n", "5.738793548317167\n", "5.000000000010186\n"]}], "source": ["class Point :\n", " def __init__ (moi, x,y) :\n", " moi.x = x\n", " moi.y = y\n", " def norm(moi, lx = 2) :\n", " return (abs(moi.x)**lx + abs(moi.y)**lx) ** (1.0 / lx)\n", " \n", "p = Point(4,5) \n", "print (p.norm(1))\n", "print (p.norm(2))\n", "print (p.norm(3))\n", "print (p.norm(100))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["On peut bien s\u00fbr appeler une m\u00e9thode de la classe depuis une autre m\u00e9thode de la m\u00eame classe :"]}, {"cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["True\n", "False\n"]}], "source": ["class Point :\n", " def __init__ (moi, x,y) :\n", " moi.x = x\n", " moi.y = y\n", " def norm(moi, lx = 2) :\n", " if lx == 0 : return moi.est_nul()\n", " else : return (abs(moi.x)**lx + abs(moi.y)**lx) ** (1.0 / lx)\n", " def est_nul(moi):\n", " return moi.x == 0 and moi.y == 0\n", " \n", "p0 = Point(0,0)\n", "p = Point(0,4)\n", "print(p0.est_nul())\n", "print(p.est_nul())"]}, {"cell_type": "markdown", "metadata": {}, "source": ["### Exercice 2 : \u00e0 faire \u00e0 trois, carr\u00e9 magique (suite)\n", "\n", "Ajouter trois m\u00e9thodes \u00e0 la classe carr\u00e9 magique :\n", "\n", "- une m\u00e9thode qui compte la somme des nombres sur chaque ligne, colonne, diagonale\n", "- une m\u00e9thode qui dit si tous les chiffres du carr\u00e9s sont uniques,\n", "- une m\u00e9thode qui dit si le carr\u00e9 est magique."]}, {"cell_type": "markdown", "metadata": {}, "source": ["### Exercice 3 : trouver tous les carr\u00e9s magiques\n", "\n", "On peut d\u00e9composer ce probl\u00e8me en deux \u00e9tapes :\n", "\n", "- Consid\u00e9rer un ensemble de carr\u00e9s qui inclut l'ensemble des carr\u00e9s magiques\n", "- Parcourir cet ensemble et m\u00e9moriser dans une liste ceux qui sont magiques."]}, {"cell_type": "markdown", "metadata": {}, "source": ["### Exercice 4 : faire plus rapide\n", " \n", "La vitesse de la fonction d\u00e9pend de l'ensemble de d\u00e9part qui peut contenir $9^9$ possibilit\u00e9s, ou encore $9!$. \n", "\n", "- A quoi correspondent ces nombres ? \n", "- Peut-on faire plus rapide encore ?"]}, {"cell_type": "code", "execution_count": 17, "metadata": {"collapsed": true}, "outputs": [], "source": []}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.1"}}, "nbformat": 4, "nbformat_minor": 2}