Autour du codehttp://autourducode.com/2017-09-15T08:45:00+02:00Distribuer un paquet python via pypi2017-09-15T08:45:00+02:002017-09-15T08:45:00+02:00Vincent Mailloltag:autourducode.com,2017-09-15:/distribuer-paquet-python-pypi-pip-wheel.html<p class="first last">Vous souhaitez distribuer votre projet python sur pypi pour en faire bénéficier la communauté, suivez le guide.</p> <p>Nous allons voir comment distribuer un projet python sur <strong>pypi</strong> pour que n'importe qui puisse l'installer en utilisant la commande pip install.</p> <div class="section" id="pypi-c-est-quoi"> <h2>Pypi, c'est quoi ?</h2> <p>Pypi signifie Python Package Index. C'est un site dont le but est de recenser et de mettre à disposition de la communauté des paquets Python. Il est accecible à l'addresse pypi.python.org</p> </div> <div class="section" id="organiser-son-projet-pour-le-distribuer"> <h2>Organiser son projet pour le distribuer</h2> <p>Dans cet exemple, on va distribuer un projet fictif nommé <em>felkafe</em>. Voici comment celui-ci est organisé.</p> <pre class="literal-block"> . ├── docs ├── felkafe │&nbsp;&nbsp; ├── __init__.py │&nbsp;&nbsp; ├── __main__.py │&nbsp;&nbsp; ├── module.py │&nbsp;&nbsp; └── other_module.py ├── LICENSE.txt ├── README.rst ├── setup.py └── tests ├── __init__.py ├── module.py └── other_module.py </pre> <p>Le répertoire felkafe est votre packet python. Ici on utilise un packet, mais rien ne vous empêche d'avoir un seul module. C'est juste une question d'organisation du code. Si votre module est trop gros (plus de 1000 lignes pour pylint) il est préférable de le décomposer.</p> <p>Les deux organisations ci-dessous sont complètement équivalentes</p> <p>Dans un seul module:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">felkafe.py</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="n">PY</span> <span class="o">=</span> <span class="mf">3.14</span> <span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span> <span class="k">print</span><span class="p">(</span><span class="s1">'PY: {}'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">PY</span><span class="p">))</span> </pre> </div> </div> </div> <p>Dans un package:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-2">felkafe/__init__.py</a></li> <li><a class="reference internal" href="#code-3">felkafe/__main__.py</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="n">PY</span> <span class="o">=</span> <span class="mf">3.14</span> </pre> </div> </div> <div class="tab-pane" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="kn">from</span> <span class="nn">.</span> <span class="kn">import</span> <span class="n">PY</span> <span class="k">print</span><span class="p">(</span><span class="s1">'PY: {}'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">PY</span><span class="p">))</span> </pre> </div> </div> </div> <p>Dans les 2 cas vous pourrez écrire <kbd class="kbd"> from felkafe import PY</kbd> dans d'autre projet python, ou exécuter votre module via la commande <kbd class="kbd"> python -m felkafe</kbd> pour afficher <kbd class="kbd"> PY: 3.14</kbd> </p> <p>Le répertoire <strong>docs</strong> contient la documentation.</p> <p>Le répertoire <strong>tests</strong> contient les tests unitaires de votre projet, ce répertoire n'a pas à être distribué. Les tests doivent être exécutés par le développeur du paquet pas par l'utilisateur du paquet</p> <p>Les fichiers suivants sont obligatoires:</p> <p><strong>LICENSE.txt</strong> contient les détails de la licence que vous avez choisie pour distribuer votre logiciel. Sans ce fichier dans votre paquet, le droit d'auteur s'applique et la redistribution devient illégale. Pypi ne distribue donc pas de package sans licence. Si vous ne savez pas quelle licence choisir, voici un <a class="reference external" href="https://www.scriptol.fr/logiciel/licences.php">tableau des licences les plus connues</a>.</p> <p><strong>README.rst</strong> Ce fichier peut être écrit en <a class="reference external" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> sinon un README.txt fera l'affaire. L'important, c'est qu'il contienne le nom de votre package, ce qu'il fait, comment l'installer, un petit exemple et un lien vers la doc.</p> </div> <div class="section" id="le-fichier-setup-py"> <h2>Le fichier setup.py</h2> <p>Le fichier setup.py est le point d'entrée du programme chargé de transformer votre paquet en distribuable.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-4">setup.py</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="kn">from</span> <span class="nn">setuptools</span> <span class="kn">import</span> <span class="n">setup</span> <span class="n">setup</span><span class="p">(</span> <span class="n">name</span><span class="o">=</span><span class="s1">'felkafe'</span><span class="p">,</span> <span class="n">version</span><span class="o">=</span><span class="s1">'1.0.1'</span><span class="p">,</span> <span class="n">description</span><span class="o">=</span><span class="s1">'distribution example'</span><span class="p">,</span> <span class="n">keywords</span><span class="o">=</span><span class="s1">'distribution pypi setup.py'</span><span class="p">,</span> <span class="n">author</span><span class="o">=</span><span class="s1">'felkafe teams'</span><span class="p">,</span> <span class="n">author_email</span><span class="o">=</span><span class="s1">'teams&#64;felkafe.com'</span><span class="p">,</span> <span class="n">url</span><span class="o">=</span><span class="s1">'https://github.com/felkafe/felkafe'</span><span class="p">,</span> <span class="n">license</span><span class="o">=</span><span class="s1">'GPLv3'</span><span class="p">,</span> <span class="n">packages</span><span class="o">=</span><span class="p">[</span><span class="s1">'felkafe'</span><span class="p">],</span> <span class="n">classifiers</span><span class="o">=</span><span class="p">[</span> <span class="s1">'Development Status :: 3 - Alpha'</span><span class="p">,</span> <span class="s1">'Intended Audience :: Developers'</span><span class="p">,</span> <span class="s1">'Programming Language :: Python :: 3.4'</span><span class="p">,</span> <span class="s1">'Programming Language :: Python :: 3.5'</span><span class="p">,</span> <span class="s1">'Programming Language :: Python :: 3.6'</span><span class="p">,</span> <span class="s1">'Topic :: Software Development :: Testing'</span><span class="p">,</span> <span class="s1">'License :: OSI Approved :: GNU General Public License v3 (GPLv3)'</span> <span class="p">],</span> <span class="n">python_requires</span><span class="o">=</span><span class="s1">'&gt;=3.4'</span><span class="p">,</span> <span class="n">install_requires</span><span class="o">=</span><span class="p">[</span><span class="s1">'voluptuous'</span><span class="p">]</span> <span class="p">)</span> </pre> </div> </div> </div> <p>On va décrire quelques-uns de ces paramètres:</p> <div class="section" id="version"> <h3>version</h3> <p>Ce champ doit contenir la version de votre paquet et son format doit respecter la <a class="reference external" href="https://www.python.org/dev/peps/pep-0440/">PEP 440</a>. Pour simplifier grandement la vingtaine de pages de la PEP 440. Sachez que 2 ou 3 nombres séparés par des points est un format de version valide.</p> <p>Si vous souhaitez importer la valeur de ce champ directement de votre paquet, sachez que cela est très risqué.</p> <p>Generalement on met une variable <strong>__version__</strong> dans le __init__.py qui contient la version du paquet. Cette variable est une convention pris en exemple dans la <a class="reference external" href="https://www.python.org/dev/peps/pep-0008/">PEP 8</a>.</p> <p>Si par exemple le module 'felkafe.module' contient la classe Bidule, et que vous souhaitez que Bidule soit directement importable de felkafe. Votre __init__.py va resembler à ceci:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-5">felkafe/__init__.py</a></li> <li><a class="reference internal" href="#code-6">felkafe/module.py</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-5"> <div class="highlight"> <pre class="literal-block"> <span class="kn">from</span> <span class="nn">.module</span> <span class="kn">import</span> <span class="n">Bidule</span> <span class="n">__version__</span> <span class="o">=</span> <span class="s1">'1.0.1'</span> <span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'__version__'</span><span class="p">,</span> <span class="s1">'Bidule'</span><span class="p">]</span> </pre> </div> </div> <div class="tab-pane" id="code-6"> <div class="highlight"> <pre class="literal-block"> <span class="kn">import</span> <span class="nn">voluptuous</span> <span class="k">class</span> <span class="nc">Bidule</span><span class="p">:</span> <span class="o">...</span> </pre> </div> </div> </div> <p>Le problème c'est que lors de l'installation de votre paquet le <strong>setup.py</strong> va être lu et il va importer <strong>felkafe.__version__</strong>, mais votre <strong>__init__.py</strong> en important Bidule va devoir importer tous les modules tiers utilisés dans <strong>felkafe.module</strong>. En gros le setup.py ne peut pas fonctionner si l'utilisateur n'a pas déjà installé les modules qui devraient être automatiquement installés.</p> <p>Une solution et de faire un module <strong>felkafe.__version__.py</strong> et dans le <strong>setup.py</strong> vous importez __version__.py en fessant comme si felkafe était un répertoire normal et non un paquet python.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-7">setup.py</a></li> <li><a class="reference internal" href="#code-8">felkafe/__version__.py</a></li> <li><a class="reference internal" href="#code-9">felkafe/__init__.py</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-7"> <div class="highlight"> <pre class="literal-block"> <span class="kn">from</span> <span class="nn">setuptools</span> <span class="kn">import</span> <span class="n">find_packages</span><span class="p">,</span> <span class="n">setup</span> <span class="kn">import</span> <span class="nn">sys</span> <span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="s1">'./felkafe'</span><span class="p">)</span> <span class="kn">from</span> <span class="nn">__version__</span> <span class="kn">import</span> <span class="n">__version__</span> <span class="n">setup</span><span class="p">(</span> <span class="n">name</span><span class="o">=</span><span class="s1">'felkafe'</span><span class="p">,</span> <span class="n">version</span><span class="o">=</span><span class="n">__version__</span><span class="p">,</span> <span class="o">...</span><span class="p">)</span> </pre> </div> </div> <div class="tab-pane" id="code-8"> <div class="highlight"> <pre class="literal-block"> <span class="n">__version__</span> <span class="o">=</span> <span class="s1">'1.0.1'</span> </pre> </div> </div> <div class="tab-pane" id="code-9"> <div class="highlight"> <pre class="literal-block"> <span class="kn">from</span> <span class="nn">.__version__</span> <span class="kn">import</span> <span class="n">__version__</span> <span class="kn">from</span> <span class="nn">.module</span> <span class="kn">import</span> <span class="n">Bidule</span> <span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'__version__'</span><span class="p">,</span> <span class="s1">'Bidule'</span><span class="p">]</span> </pre> </div> </div> </div> </div> <div class="section" id="keywords"> <h3>keywords</h3> <p>Une liste de mots décrivant le projet séparé par des espaces.</p> </div> <div class="section" id="packages"> <h3>packages</h3> <p>La liste contenant les packets à distribuer. Si vous écrivez cette liste à la main, il faut garder à l'esprit que les sous-paquets ne sont pas inclus. Cela signifie que si votre application évolue comme ceci:</p> <pre class="literal-block"> . ├── felkafe │&nbsp;&nbsp; ├── __init__.py │&nbsp;&nbsp; ├── module.py │&nbsp;&nbsp; ├── other_module.py │&nbsp;&nbsp; └── subpackage │&nbsp;&nbsp; ├── __init__.py │&nbsp;&nbsp; └── submodule.py ├── LICENSE.txt ├── README.rst └── setup.py </pre> <p>Le champs packages du setup.py devra contenir ['felkafe', 'felkafe.subpackage'] pour ne pas distribuer un paquet incomplet. Vous pouvez si vous le souhaitez automatiser la recherche des packet en utilisant setuptools.find_packages. La fonction find_packages va lister tous les packages et sub-package qu'elle trouve. Si vous avez par exemple un package test à la racine de votre projet, il va donc être listé aussi. Pour éviter ça, vous pouvez utiliser le paramètre exclude.</p> <p>Il faudra donc penser à le mettre à jour à chaque fois que vous ajouter un package dans votre projet qui n'est pas destiné à être distribué telle que des plugins perso pour votre fabfile ou des tests en plus. Pour ne plus avoir à mettre à jour le champ package, une possibilité est d'utiliser le patron de code suivant:</p> <pre class="literal-block"> packages=['felkafe'] + [ 'felkafe.{}'.format(subpackage) for subpackage in find_packages(where='./felkafe') ] </pre> <p>Avec ce patron, vous n'aurais plus à modifier votre setup.py sauf si vous souhaitez distribuer plusieurs paquets distincts au sein d'un même projet.</p> </div> <div class="section" id="classifiers"> <h3>classifiers</h3> <p>Les classifiers permettent de trier les projets sur Pypi. Voici la <a class="reference external" href="https://pypi.python.org/pypi?%3Aaction=list_classifiers">liste des classifiers</a> que vous pouvez utiliser.</p> </div> <div class="section" id="python-requires"> <h3>python_requires</h3> <p>Indique quelle version de python est requise pour utiliser votre paquet. C'est le numéro de la version précédé par l'une des clause suivante.</p> <table border="1" class="docutils"> <colgroup> <col width="13%" /> <col width="88%" /> </colgroup> <thead valign="bottom"> <tr><th class="head">Clause</th> <th class="head">Signification</th> </tr> </thead> <tbody valign="top"> <tr><td>==X.Y.Z</td> <td>La version doit être X.Y.Z</td> </tr> <tr><td>&gt;=X.Y.Z</td> <td>La version doit être la X.Y.Z ou une version supérieure</td> </tr> <tr><td>&gt;X.Y.Z</td> <td>La version doit être une version supérieure à la X.Y.Z</td> </tr> <tr><td>!=X.Y.Z</td> <td>La version ne doit pas être la version X.Y.Z</td> </tr> <tr><td>&lt;=X.Y.Z</td> <td>La version doit être inférieure ou être la version X.Y.Z</td> </tr> <tr><td>&lt;X.Y.Z</td> <td>La version doit être inférieure à la version X.Y.Z</td> </tr> </tbody> </table> <p>Vous pouvez utiliser des étoiles comme jocker:</p> <pre class="literal-block"> !=3.* aucune version 3 n'est supporté </pre> <p>Et vous pouvez mettre plusieurs clauses séparées par des virgules</p> <p>La version de python 2.7 ou 3.5 et supérieure:</p> <pre class="literal-block"> '&gt;=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, &lt;4' </pre> </div> <div class="section" id="install-requires"> <h3>install_requires</h3> <p>Ce champ est utilisé par pip pour installer les autres paquets que votre paquet à besoin pour fonctionner. Il doit contenir la liste minimale des paquets à installer pour que votre paquet puisse fonctionner. C'est une liste qui prend en paramètre des chaînes de caractère au même format que les clauses dans <strong>python_requires</strong>.</p> <p>Exemple d'un paquet qui besoin de <strong>voluptuous</strong> dans sa version 1.10 ou supérieure et de <strong>attrs</strong> dans une version entre 17 et 20:</p> <pre class="literal-block"> install_requires=[ 'voluptuous&gt;=1.10', 'attrs&gt;=17, attrs&lt;20' ] </pre> <p>Les prérequis ne devraient pas être bornés à des version uniques, mais au contraire être le plus large possible.</p> <p>On dit que c'est est une liste de prérequis abstrait dans le sens où on ne spécifie pas précisément une version ou un lieu de téléchargement. Ce sera le rôle de la commande pip qui précisera l'index où récupérer le paquet et la version précise du paquet.</p> <p>Par défaut pip récupère la dernière version stable trouvée sur pypi. Si vous avez un fichier <strong>requirements.txt</strong>, évitez de dupliquer automatiquement le contenu de ce fichier dans le <strong>install_requires</strong>. Le <strong>requirements.txt</strong> est simplement une liste de paramètres pour pip. Ainsi <strong>requirements.txt</strong> est destiné à définir les prérequis pour un environnement Python complet. Vous allez pouvoir fixer des versions précises pour rendre une installation répétable. Votre <strong>requirements.txt</strong> pourra par exemple contenir un prérequis vers un framework de test qu'utilisera la CI pour lancer les tests. Cela n'a pas lieu d'être dans le <strong>install_requires</strong>, car vous ne distribuez pas les tests avec votre paquet.</p> </div> </div> <div class="section" id="transformer-son-paquet-en-distribuable"> <h2>Transformer son paquet en distribuable</h2> <p>Premierement on va s'assurer d'avoir la dernière version de pip, setuptools et twine.</p> <p><kbd class="kbd"> $ pip install -U pip twine setuptools</kbd> </p> <p>On peut distribuer sous forme de source via la commande suivante.</p> <p><kbd class="kbd"> $ python setup.py sdist</kbd> </p> <p>Ou sous forme de Wheel via la commande:</p> <p><kbd class="kbd"> $ python setup.py bdist_wheel</kbd> </p> </div> <div class="section" id="uploader-son-paquet"> <h2>Uploader son paquet</h2> <p>Pypi possède une zone de test où les projets déposés son supprimer toutes les 24 heures. On va utiliser cette zone de teste pour ne pas polluer le pypi publique avec des librairies inutiles.</p> <p>Premierement, enregistrer son projet sur le <a class="reference external" href="https://testpypi.python.org/pypi?:action=register_form">pypi de test</a></p> <p>Un lien de confirmation vous sera envoyé par email.</p> <p>On va ensuite uploader le packet via la command suivante:</p> <p><kbd class="kbd"> twine upload --repository-url https://test.pypi.org/legacy/ dist/*</kbd> </p> <p>Si vous souhaitez ne plus avoir à saisir votre nom d'utilisateur et votre mot de passe à chaque fois, vous pouvez créer un fichier <strong>.pypirc</strong> de configuration dans votre home</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-10">~/.pypirc </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-10"> <div class="highlight"> <pre class="literal-block"> <span class="k">[distutils]</span> <span class="na">index-servers</span> <span class="o">=</span><span class="s"> pypi pypitest</span> <span class="k">[pypi]</span> <span class="na">repository:https://pypi.python.org/pypi</span> <span class="na">username:votre-nom</span> <span class="na">password:votre-mot-de-pass</span> <span class="k">[pypitest]</span> <span class="na">repository:https://testpypi.python.org/pypi</span> <span class="na">username:votre-nom</span> <span class="na">password:votre-mot-de-pass</span> </pre> </div> </div> </div> <dl class="docutils"> <dt>Avec ce fichier pour publier sur le server de test:</dt> <dd><kbd class="kbd"> twine upload --repository pypitest</kbd> </dd> <dt>Avec ce fichier pour publier sur le server standard:</dt> <dd><kbd class="kbd"> twine upload --repository pypi</kbd> </dd> </dl> </div> <div class="section" id="tester-l-installation-de-son-package"> <h2>Tester l'installation de son package</h2> <p>Si vous avez envoyé votre paquet sur le <strong>pypitest</strong> vous pouvez le récupérer comme ceci:</p> <p><kbd class="kbd"> pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple felkafe</kbd> </p> <p>Le premier paramètre <strong>--index-url</strong> indique ou allez chercher le paquet, le paramètre <strong>--extra-index-url</strong> permet à pip d'aller chercher sur le dépôt officiel les paquets qu'il ne trouverai pas sur le dépot de test. C'est utile si votre paquêt à besoin d'installer des dépendances.</p> <p>Allez à vos IDE et bon code! <img alt="smile" src="/theme/img/smiley/smiley-smile.gif" /></p> </div> Déployer une application WSGI en utilisant uWSGI et Nginx2017-07-24T22:10:00+02:002017-07-24T22:10:00+02:00Vincent Mailloltag:autourducode.com,2017-07-24:/deployer-une-application-python-WSGI-avec-uWSGI-et-Nginx.html<p class="first last">Dans cet article, nous allons voir comment déployer une application python WSGI en utilisant le serveur uWSGI et le serveur Nginx.</p> <p>Dans cet article, nous allons voir comment déployer une application python WSGI en utilisant <a class="reference external" href="https://uwsgi-docs.readthedocs.io/en/latest/">le serveur uWSGI</a> et <a class="reference external" href="https://nginx.org/">le serveur Nginx</a>.</p> <p>Le but de Nginx et de servir de porte d'entrée. C'est lui qui recevra les requêtes en premier, il pourra alors servir directement les ressources statiques comme les fichiers CSS, les images ou les scripts JS, ou alors retransmettre la requête à uWSGI. uWSGI récupérera les requêtes venant de Nginx pour les passer aux applications après les avoir transformées en objets python. uWSGI utilisera <a class="reference external" href="http://uwsgi-docs.readthedocs.io/en/latest/Protocol.html">le protocole uwsgi</a> (en minuscule) pour communiquer avec Nginx.</p> <div class="section" id="protocole-http-ou-uwsgi-quelle-est-la-difference"> <h2>Protocole HTTP ou uwsgi quelle est la différence ?</h2> <p>On va pouvoir choisir la façon de communiquer entre Nginx et uWSGI. Habituellement, Nginx envoie la requête directement en HTTP vers uWSGI, mais il peut également utiliser le protocole uwsgi (en minuscule). Cela ne change pas grand chose d'utiliser l'un ou l'autre niveau performance, que la requête soit transformée par Nginx ou pas uWSGI le travail devra être fait. Par contre le protocole uwsgi permet le passage de variables supplémentaires qui peut être utilisées si l'on souhaite faire des hôtes virtuels par exemple.</p> </div> <div class="section" id="installation-de-python3-et-nginx"> <h2>Installation de python3 et Nginx</h2> <p>On va partir du Debian 9 en commençant par installer python</p> <p><kbd class="kbd"> apt-get update</kbd> <kbd class="kbd"> apt-get install build-essential python3 python3-dev</kbd> </p> <p>Puis l'on va installer Nginx</p> <p><kbd class="kbd"> apt-get install nginx nginx-full</kbd> </p> <p>ainsi que l'utilitaire wget:</p> <p><kbd class="kbd"> apt-get install wget</kbd> </p> <p>On va créer l'utilisateur <em>app-manager</em> qui sera le propriétaire de l'application.</p> <p><kbd class="kbd"> useradd --shell /bin/bash -m --base-dir /var/www app-manager</kbd> </p> <p>Connectons-nous avec ce nouvel utilisateur, puis créons un environnement virtuel dans son répertoire home:</p> <p><kbd class="kbd"> su - app-manager</kbd> <kbd class="kbd"> python3 -m venv wsgi-venv --without-pip</kbd> <kbd class="kbd"> source wsgi-venv/bin/activate</kbd> <kbd class="kbd"> wget -qO - https://bootstrap.pypa.io/get-pip.py | python -</kbd> </p> <p>On va installer uWSGI via pip et désactiver notre environnement virtuel. <kbd class="kbd"> pip install uwsgi</kbd> <kbd class="kbd"> deactivate</kbd> </p> <p>Créer deux répertoires <em>app</em> qui contiendra l'application python et <em>app-venv</em> qui contiendra l'environnement virtuel de l'application.</p> </div> <div class="section" id="installation-de-l-application-python"> <h2>Installation de l'application python</h2> <p>Pour l'exemple, j'ai créé une petite application nommée <em>app_1</em> en utilisant le micro-framework Bottle l'application affiche une page web affichant avec la version de Bottle utiliser.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">app_1/app.py </a></li> <li><a class="reference internal" href="#code-2">app_1/wsgi.py </a></li> <li><a class="reference internal" href="#code-3">app_1/requirements.txt</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="kn">import</span> <span class="nn">bottle</span> <span class="nd">&#64;bottle</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/'</span><span class="p">)</span> <span class="k">def</span> <span class="nf">home</span><span class="p">():</span> <span class="k">return</span> <span class="s1">'''&lt;html&gt; &lt;head&gt;&lt;/head&gt; &lt;body&gt;Hello Bottle </span><span class="si">{}</span><span class="s1"> !&lt;/body&gt; &lt;/html&gt;'''</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">bottle</span><span class="o">.</span><span class="n">__version__</span><span class="p">)</span> <span class="n">bottle</span><span class="o">==</span><span class="mf">0.11</span><span class="o">.</span><span class="mi">1</span> </pre> </div> </div> <div class="tab-pane" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="kn">import</span> <span class="nn">bottle</span> <span class="kn">import</span> <span class="nn">app</span> <span class="n">application</span> <span class="o">=</span> <span class="n">bottle</span><span class="o">.</span><span class="n">default_app</span><span class="p">()</span> </pre> </div> </div> <div class="tab-pane" id="code-3"> <div class="highlight"> <pre class="literal-block"> bottle<span class="o">==</span><span class="mi">0</span><span class="p">.</span><span class="mi">11</span><span class="p">.</span><span class="mi">1</span> </pre> </div> </div> </div> <p>On va copier <em>app_1</em> dans le répertoire <em>/var/www/app-manager/app/</em></p> <p>Voici à quoi ressemble notre arborescence</p> <pre class="literal-block"> |-- app | +-- app_1 | |-- app.py | |-- requirements.txt | +-- wsgi.py |-- app-venv +-- wsgi-venv </pre> <p>On va créer un environnement virtuel et installer les dépendances de notre application.</p> <p><kbd class="kbd"> python3 -m venv app-venv/app_1 --without-pip</kbd> <kbd class="kbd"> source app-venv/app_1/bin/activate</kbd> <kbd class="kbd"> wget -qO - https://bootstrap.pypa.io/get-pip.py | python -</kbd> <kbd class="kbd"> pip install -r app/app_1/requirements.txt</kbd> <kbd class="kbd"> deactivate</kbd> </p> </div> <div class="section" id="configuration-de-uwsgi"> <h2>Configuration de uWSGI</h2> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-4">uwsgi.ini</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="k">[uwsgi]</span> <span class="na">chdir</span> <span class="o">=</span> <span class="s">/var/www/app-manager/app/app_1</span> <span class="na">home</span> <span class="o">=</span> <span class="s">/var/www/app-manager/app-venv/app_1</span> <span class="na">module</span> <span class="o">=</span> <span class="s">wsgi:application</span> <span class="na">uid</span> <span class="o">=</span> <span class="s">app-manager</span> <span class="na">gid</span> <span class="o">=</span> <span class="s">app-manager</span> <span class="na">socket</span> <span class="o">=</span> <span class="s">/var/www/app-manager/uwsgi.sock</span> <span class="na">chmod-socket</span><span class="o">=</span><span class="s">660</span> <span class="na">master</span> <span class="o">=</span> <span class="s">true</span> <span class="na">vacuum</span> <span class="o">=</span> <span class="s">true</span> </pre> </div> </div> </div> <p><em>chdir</em> indique à uWSGI quel répertoire de travail utiliser. <em>home</em> indique quel environnement python utiliser <em>uid</em> et <em>gid</em> demandent à uWSGI d'utiliser l'utilisateur et le group <em>app-manager</em> pour exécuter les applications. <em>socket</em> et <em>chmod-socket</em> va créer une socket de type unix pour communiquer avec Nginx. Ce type de socket est dédié à la communication entre processus hébergé sur une machine. Elles sont plus rapide est plus sûr pour communiquer qu'une socket TCP/IP. <em>vacuum</em> demande à uWSGI de tenter de supprimer la socket au moment où il s'arrète.</p> <p>Il nous reste plus qu'à lancer notre serveur uWSGI, on utilisera le paramètre <em>-d</em> pour indiquer le nom du fichier où seront écrit le logs et lancer uWSGI en mode daemon.</p> <p><kbd class="kbd"> source wsgi-venv/bin/activate</kbd> <kbd class="kbd"> uwsgi -d log --ini uwsgi.ini</kbd> </p> </div> <div class="section" id="configuration-de-nginx"> <h2>Configuration de Nginx</h2> <p>Repassons en root. Il va falloir permettre à Nginx de lire et écrire sur la socket créé par uWSGI. Pour cela, on va ajouter l'utilisateur <em>www-data</em> au group <em>app-manager</em></p> <p><kbd class="kbd"> usermod -aG app-manager www-data</kbd> </p> <p>Puis l'on va créer le fichier de configuration de notre site</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-5">/etc/nginx/sites-available/app-manager </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-5"> <div class="highlight"> <pre class="literal-block"> server { listen 80; location / { include uwsgi_params; uwsgi_pass unix:/var/www/app-manager/uwsgi.sock; } } </pre> </div> </div> </div> <p>Maintenant que l'on a fini notre configuration de site, on va l'activer en créant un lien symbolique dans le répertoire <em>sites-enabled</em></p> <p><kbd class="kbd"> ln -s /etc/nginx/sites-available/app-manager /etc/nginx/sites-enabled/app-manager</kbd> <kbd class="kbd"> nginx -s reload</kbd> </p> </div> <div class="section" id="en-cas-de-problemes"> <h2>En cas de problèmes</h2> <p>Si jamais ça ne tourne pas comment vous le souhaitez, voici la liste des choses à faire pour trouver le problème.</p> <p>Commençais par voir si votre application python fonctionne en lançant uWSGI avec l'option <em>--http</em></p> <p><kbd class="kbd"> uwsgi --http :9090 --wsgi wsgi:application --pyhome /var/www/app-manager/app-venv/app_1 -d log --chdir /var/www/app-manager/app/app_1/</kbd> </p> <p>Les options <em>--wsgi</em>, <em>--pyhome</em> et <em>--chdir</em> correspondent respectivement aux options <em>module</em>, <em>home</em> et <em>chdir</em> dans le fichier <em>usgi.ini</em> Jetez un coup d'œil dans les logs de uWSGI</p> <p><kbd class="kbd"> cat log</kbd> </p> <p>Si uWSGI n'affiche aucun message d'erreur vérifier que la page de votre application s'affiche bien. <kbd class="kbd"> $ wget -O- 127.0.0.1:9090</kbd> </p> <p>S'il n'y a rien à signaler, le problème se trouve peut-être au niveau de la configuration de Nginx. Consultez alors les logs de Nginx</p> <p><kbd class="kbd"> cat /var/log/nginx/error.log</kbd> </p> </div> Le patron de conception Reactor2016-10-12T18:00:00+02:002016-10-12T18:00:00+02:00Vincent Mailloltag:autourducode.com,2016-10-12:/Le-patron-de-conception-Reactor.html<p class="first last">Le patron de conception Reactor est la base des boucle d'évènement utilisé dans les frameworks asynchrones</p> <p>Le patron de conception Reactor est la base des boucle d'évènement utilisé dans les frameworks asynchrones. Il permet de notifier des actions lorsqu'une ressource est prête à être utilisée. Une ressource peut par exemple être un fichier et une action l'affichage du contenu du fichier lorsqu'il sera prêt à être lu.</p> <div class="section" id="structure-du-design-pattern-reactor"> <h2>Structure du design pattern reactor</h2> <p>Ce patron de conception fait collaborer les quatre classes suivantes:</p> <div class="section" id="handle"> <h3>Handle</h3> <p>Cette classe est un adaptateur pour les ressources du système d'exploitation tel que les fichiers ou les sockets. Elle permet d'avoir une interface commune a toutes les ressources.</p> </div> <div class="section" id="synchronous-event-demultiplexer"> <h3>Synchronous Event Demultiplexer</h3> <p>Cette classe est un adaptateur à <strong>la librairie de démultiplexage d'entrée sortie</strong> du système d'exploitation. Le rôle de cette classe est d'offrir une interface pour surveiller un ensemble de <a class="reference internal" href="#handle">Handle</a> et d'indiquer quel Handles est près à subir immédiatement des opérations sans que cela ne bloque le système. Lorsqu'un handle est près, cette classe en informe l'<a class="reference internal" href="#initiation-dispatcher">Initiation Dispatcher</a></p> <p>exemple: Si un mail est un Handle, Synchronous Event Demultiplexer pourra surveiller ce mail et me dire lorsqu'il arrive. Je pourrai alors lire le mail immédiatement sans bloquer le système.</p> </div> <div class="section" id="initiation-dispatcher"> <h3>Initiation Dispatcher</h3> <p>Cette classe enregistre les <a class="reference internal" href="#event-handler">Event Handler</a> via sa méthode <strong>register_handler</strong>. Elle doit pouvoir associer chaque <a class="reference internal" href="#event-handler">Event Handler</a> à l'<a class="reference internal" href="#handle">Handle</a> qu'il contient. Cette classe possède une méthode <strong>handle_events</strong> qui utilise le <a class="reference internal" href="#synchronous-event-demultiplexer">Synchronous Event Demultiplexer</a> pour ce mettre en pause jusqu'à ce qu'un <a class="reference internal" href="#handle">Handle</a> soit prêt. Lorsqu'un <a class="reference internal" href="#handle">Handle</a> est prêt, elle déclenche l'<a class="reference internal" href="#event-handler">Event Handler</a> correspondant.</p> </div> <div class="section" id="event-handler"> <h3>Event Handler</h3> <p>Cette classe servira de classe de base à l'utilisateur pour définir des actions lorsqu'un <a class="reference internal" href="#handle">Handle</a> est prêt. Cela se fait en redéfinissant la méthode <strong>handle_event</strong> qui est appelée par l'<a class="reference internal" href="#initiation-dispatcher">Initiation Dispatcher</a> lorsque le <a class="reference internal" href="#handle">Handle</a> est prêt.</p> </div> </div> <div class="section" id="implementation-naive"> <h2>Implementation naïve</h2> <p>Pour le SynchronousEventDemultiplexer, il est déjà implémenté sous python. Le module porte le nom de selectors et fait déjà office d'adaptateur, nous n'avons pas à nous soucier de savoir quelle librairie de multiplexage d'entrée-sortie et disponible sur l'OS selectors nous fournie une interface et se débrouille avec ce qu'il trouve.</p> <p>La classe <strong>Selector</strong> enregistre des objets semblables à des fichiers (file-like) notre classe Handler aura donc une méthode <strong>fileno</strong> pour être compatible avec Selector. La classe InitiationDispatcher stocke les EventHandler dans un dictionnaire.</p> <p>Voici une première implémentation naïve</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">reactor.py </a></li> <li><a class="reference internal" href="#code-2">handles.py </a></li> <li><a class="reference internal" href="#code-3">main.py </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="kn">from</span> <span class="nn">selectors</span> <span class="k">import</span> <span class="n">DefaultSelector</span> <span class="k">as</span> <span class="n">SynchronousEventDemultiplexer</span> <span class="kn">from</span> <span class="nn">abc</span> <span class="k">import</span> <span class="n">abstractmethod</span><span class="p">,</span> <span class="n">ABCMeta</span> <span class="k">class</span> <span class="nc">Handle</span><span class="p">(</span><span class="n">metaclass</span><span class="o">=</span><span class="n">ABCMeta</span><span class="p">):</span> <span class="nd">&#64;abstractmethod</span> <span class="k">def</span> <span class="nf">fileno</span><span class="p">():</span> <span class="sd">&quot;&quot;&quot; return file descriptor &quot;&quot;&quot;</span> <span class="k">class</span> <span class="nc">EventHandler</span><span class="p">(</span><span class="n">metaclass</span><span class="o">=</span><span class="n">ABCMeta</span><span class="p">):</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">handle</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">handle</span> <span class="o">=</span> <span class="n">handle</span> <span class="k">def</span> <span class="nf">get_handle</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">handle</span> <span class="nd">&#64;abstractmethod</span> <span class="k">def</span> <span class="nf">handle_event</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">type_event</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">()</span> <span class="k">class</span> <span class="nc">InitiationDispatcher</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">synchronous_event_demultiplexer</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">synchronous_event_demultiplexer</span> <span class="o">=</span> <span class="n">synchronous_event_demultiplexer</span> <span class="bp">self</span><span class="o">.</span><span class="n">handlers</span> <span class="o">=</span> <span class="p">{}</span> <span class="c1">#&nbsp;{Handle: EventHandler, ...}</span> <span class="k">def</span> <span class="nf">register_handler</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">event_handler</span><span class="p">,</span> <span class="n">event_type</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">synchronous_event_demultiplexer</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">event_handler</span><span class="o">.</span><span class="n">get_handle</span><span class="p">(),</span> <span class="n">event_type</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">handlers</span><span class="p">[</span><span class="n">event_handler</span><span class="o">.</span><span class="n">get_handle</span><span class="p">()]</span> <span class="o">=</span> <span class="n">event_handler</span> <span class="k">def</span> <span class="nf">handle_events</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">handles</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">synchronous_event_demultiplexer</span><span class="o">.</span><span class="n">select</span><span class="p">()</span> <span class="k">for</span> <span class="n">handle</span><span class="p">,</span> <span class="n">event</span> <span class="ow">in</span> <span class="n">handles</span><span class="p">:</span> <span class="n">event_handler</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">handlers</span><span class="p">[</span><span class="n">handle</span><span class="o">.</span><span class="n">fileobj</span><span class="p">]</span> <span class="n">event_handler</span><span class="o">.</span><span class="n">handle_event</span><span class="p">()</span> </pre> </div> </div> <div class="tab-pane" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="kn">from</span> <span class="nn">reactor1</span> <span class="k">import</span> <span class="n">Handle</span> <span class="kn">from</span> <span class="nn">pathlib</span> <span class="k">import</span> <span class="n">Path</span> <span class="kn">import</span> <span class="nn">os</span> <span class="k">class</span> <span class="nc">FifoHandle</span><span class="p">(</span><span class="n">Handle</span><span class="p">):</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">path_to_fifo</span><span class="p">):</span> <span class="n">path</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="n">path_to_fifo</span><span class="p">)</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">path</span><span class="o">.</span><span class="n">is_fifo</span><span class="p">():</span> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s1">'</span><span class="si">{}</span><span class="s1"> is not FIFO'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">path_to_fifo</span><span class="p">))</span> <span class="bp">self</span><span class="o">.</span><span class="n">fifo</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">path_to_fifo</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">O_RDWR</span><span class="p">)</span> <span class="k">def</span> <span class="nf">fileno</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">fifo</span> <span class="k">def</span> <span class="nf">read</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">reads</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">fifo</span><span class="p">,</span> <span class="mi">2048</span><span class="p">)</span> <span class="k">return</span> <span class="n">reads</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span> </pre> </div> </div> <div class="tab-pane" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="kn">from</span> <span class="nn">reactor1</span> <span class="k">import</span> <span class="n">EventHandler</span><span class="p">,</span> <span class="n">SynchronousEventDemultiplexer</span><span class="p">,</span> <span class="n">InitiationDispatcher</span> <span class="kn">from</span> <span class="nn">selectors</span> <span class="k">import</span> <span class="n">EVENT_READ</span> <span class="kn">from</span> <span class="nn">handles</span> <span class="k">import</span> <span class="n">FifoHandle</span> <span class="k">class</span> <span class="nc">PrintFifoEventHandler</span><span class="p">(</span><span class="n">EventHandler</span><span class="p">):</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">path_to_fifo</span><span class="p">):</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">FifoHandle</span><span class="p">(</span><span class="n">path_to_fifo</span><span class="p">))</span> <span class="k">def</span> <span class="nf">handle_event</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">type_event</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">handle</span><span class="o">.</span><span class="n">read</span><span class="p">())</span> <span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span> <span class="n">synchronous_event_demultiplexer</span> <span class="o">=</span> <span class="n">SynchronousEventDemultiplexer</span><span class="p">()</span> <span class="n">initiation_dispatcher</span> <span class="o">=</span> <span class="n">InitiationDispatcher</span><span class="p">(</span><span class="n">synchronous_event_demultiplexer</span><span class="p">)</span> <span class="n">fifo_event</span> <span class="o">=</span> <span class="n">PrintFifoEventHandler</span><span class="p">(</span><span class="s1">'/tmp/my_fifo'</span><span class="p">)</span> <span class="n">initiation_dispatcher</span><span class="o">.</span><span class="n">register_handler</span><span class="p">(</span><span class="n">fifo_event</span><span class="p">,</span> <span class="n">EVENT_READ</span><span class="p">)</span> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> <span class="n">initiation_dispatcher</span><span class="o">.</span><span class="n">handle_events</span><span class="p">()</span> </pre> </div> </div> </div> <p>On a créé dans le fichier handles.py un Handle appelé <strong>FifoHandle</strong> pour manipuler les <strong>FIFO</strong>. Un FIFO est un fichier spécial qui permet à deux processus de dialoguer, le FIFO est visible comme un fichier, mais l'information n'est jamais stockée sur le disque et transite en mémoire. Pour créer un FIFO, on peut utiliser la commande mkfifo.</p> <p>Puis on instancie un SynchronousEventDemultiplexer, un InitiationDispatcher et un PrintFifoEventHandler que l'on enregistre via la méthode register_handler.</p> <p>Avant de lancer le programme, il faudra créer le FIFO avec la commande</p> <p><kbd class="kbd"> mkfifo /tmp/my_fifo</kbd> </p> <p>On pourra une fois le programme lancé écrire dans le FIFO:</p> <p><kbd class="kbd"> echo Toto &gt; /tmp/my_fifo</kbd> </p> </div> <div class="section" id="implementation-un-peu-plus-pythonique"> <h2>Implementation un peu plus pythonique</h2> <p>La méthode Selector.select retourne une liste d'objets de type SelectorKey qui possèdent les attributs suivant :</p> <ul class="simple"> <li>fileobj: Contient notre file-like objet.</li> <li>fd: Contiens le descripteur de fichier.</li> <li>events: L'évènement qui s'est produit (lecture / écriture)</li> <li>data: Un objet que l'utilisateur a fourni en troisième paramètre lors de l'appel à la méthode Selector.register</li> </ul> <p>Une des simplifications possibles est donc d'utiliser l'attribut data pour stocker l'EventHandler, nous n'avons donc plus à maintenir le dictionnaire {Handle: EventHandler}</p> <div class="section" id="gestion-des-coroutines"> <h3>Gestion des coroutines</h3> <p>Une coroutine est une fonction dont l'exécution peut être suspendue est repris. En python les coroutines sont des objets qui possède une méthode <strong>send()</strong>. On doit appeler cette méthode en lui passant <strong>None</strong>. Cela est dû au fait que historiquement, l'implémentation des coroutines est basée sur celle des generators. La méthode send retourne None ou lance une exception de type <strong>StopIteration</strong> contenant le résultat. En pyton3.5, on peut chaîner les coroutine en utilisant le mot-clé <strong>await</strong> comme ceci:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-4">Exemple de coroutine python 3.5 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="k">async</span> <span class="k">def</span> <span class="nf">coroutine_func_1</span><span class="p">():</span> <span class="k">return</span> <span class="mi">1</span> <span class="k">async</span> <span class="k">def</span> <span class="nf">coroutine_func_2</span><span class="p">():</span> <span class="n">a</span> <span class="o">=</span> <span class="k">await</span> <span class="n">coroutine_func_1</span><span class="p">()</span> <span class="n">b</span> <span class="o">=</span> <span class="k">await</span> <span class="n">coroutine_func_1</span><span class="p">()</span> <span class="k">return</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span> <span class="n">coroutine</span> <span class="o">=</span> <span class="n">coroutine_func_2</span><span class="p">()</span> <span class="n">coroutine</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="kc">None</span><span class="p">)</span> <span class="c1">#&nbsp; StopIteration: 2</span> </pre> </div> </div> </div> <p>Le <strong>mot clè await</strong> peut également être suivi d'un <strong>object awaitable</strong>, qui est un objet qui doit définir la méthode <strong>__await__</strong>. Cette méthode doit retourner un iterator dont la méthode <strong>__next__</strong> revoie None ou le résultat dans uneStopIteration. On va donc créer un awaitable qui retournera un iterator qui sera un <strong>EventHandler</strong>. lorsque la méthode handle_event sera appelée, elle indiquera à l'objet qu'au prochain appele de <strong>__next__</strong>, il peut lancer un StopIteration pour donner le résultat et de désinscrire le <strong>handle</strong> de l'initiation_dispatcher</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-5">Python3.5 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-5"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">FifoReader</span><span class="p">:</span> <span class="k">class</span> <span class="nc">FifoReaderIterator</span><span class="p">(</span><span class="n">EventHandler</span><span class="p">):</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">path_to_fifo</span><span class="p">,</span> <span class="n">initiation_dispatcher</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">handle</span> <span class="o">=</span> <span class="n">FifoHandle</span><span class="p">(</span><span class="n">path_to_fifo</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">_initiation_dispatcher</span> <span class="o">=</span> <span class="n">initiation_dispatcher</span> <span class="bp">self</span><span class="o">.</span><span class="n">_ready</span> <span class="o">=</span> <span class="kc">False</span> <span class="bp">self</span><span class="o">.</span><span class="n">_data</span> <span class="o">=</span> <span class="kc">None</span> <span class="bp">self</span><span class="o">.</span><span class="n">_initiation_dispatcher</span><span class="o">.</span><span class="n">register_handler</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">EVENT_READ</span><span class="p">)</span> <span class="k">def</span> <span class="nf">__next__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_ready</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">StopIteration</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_data</span><span class="p">)</span> <span class="k">return</span> <span class="kc">None</span> <span class="k">def</span> <span class="nf">handle_event</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_ready</span> <span class="o">=</span> <span class="kc">True</span> <span class="bp">self</span><span class="o">.</span><span class="n">_data</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">handle</span><span class="o">.</span><span class="n">read</span><span class="p">()</span> <span class="bp">self</span><span class="o">.</span><span class="n">_initiation_dispatcher</span><span class="o">.</span><span class="n">remove_handler</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">path_to_fifo</span><span class="p">,</span> <span class="n">initiation_dispatcher</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_iterator</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">FifoReaderIterator</span><span class="p">(</span><span class="n">path_to_fifo</span><span class="p">,</span> <span class="n">initiation_dispatcher</span><span class="p">)</span> <span class="k">def</span> <span class="nf">__await__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_iterator</span> </pre> </div> </div> </div> <p>On poura donc utiliser la classe FifoReader dans une coroutine comme ceci pour concaténer la sortie de deux FIFO:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-6">Python3.5 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-6"> <div class="highlight"> <pre class="literal-block"> <span class="k">async</span> <span class="k">def</span> <span class="nf">concat</span><span class="p">():</span> <span class="n">a</span> <span class="o">=</span> <span class="k">await</span> <span class="n">FifoReader</span><span class="p">(</span><span class="s1">'/tmp/my_fifo_1'</span><span class="p">,</span> <span class="n">initiation_dispatcher</span><span class="p">)</span> <span class="n">b</span> <span class="o">=</span> <span class="k">await</span> <span class="n">FifoReader</span><span class="p">(</span><span class="s1">'/tmp/my_fifo_2'</span><span class="p">,</span> <span class="n">initiation_dispatcher</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="n">a</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="o">+</span> <span class="n">b</span><span class="o">.</span><span class="n">strip</span><span class="p">())</span> </pre> </div> </div> </div> <p>On va ensuite modifier l'InitiationDispatcher. Afin qu'il puisse gérer les coroutines il va posséder une méthode <strong>add_coroutine</strong> qui tentera d'executer la coroutine et la mettra dans une liste si elle retourneNone. La méthode <strong>handle_events</strong> après avoir géré les <strong>handles</strong> tentera d'exécuter les coroutines et ne conservera que celle qui n'ont pas lancé de StopIteration.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-7">reactor.py</a></li> <li><a class="reference internal" href="#code-8">main.py </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-7"> <div class="highlight"> <pre class="literal-block"> <span class="kn">from</span> <span class="nn">selectors</span> <span class="kn">import</span> <span class="n">DefaultSelector</span> <span class="k">as</span> <span class="n">SynchronousEventDemultiplexer</span> <span class="kn">from</span> <span class="nn">selectors</span> <span class="kn">import</span> <span class="n">EVENT_READ</span> <span class="kn">from</span> <span class="nn">abc</span> <span class="kn">import</span> <span class="n">abstractmethod</span><span class="p">,</span> <span class="n">ABCMeta</span> <span class="k">class</span> <span class="nc">Handle</span><span class="p">(</span><span class="n">metaclass</span><span class="o">=</span><span class="n">ABCMeta</span><span class="p">):</span> <span class="nd">&#64;abstractmethod</span> <span class="k">def</span> <span class="nf">fileno</span><span class="p">():</span> <span class="sd">&quot;&quot;&quot; return file descriptor &quot;&quot;&quot;</span> <span class="k">class</span> <span class="nc">EventHandler</span><span class="p">:</span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">handle</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">handle</span> <span class="o">=</span> <span class="n">handle</span> <span class="k">def</span> <span class="nf">get_handle</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">handle</span> <span class="k">def</span> <span class="nf">handle_event</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">type_event</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">()</span> <span class="k">class</span> <span class="nc">InitiationDispatcher</span><span class="p">:</span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">synchronous_event_demultiplexer</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">synchronous_event_demultiplexer</span> <span class="o">=</span> <span class="n">synchronous_event_demultiplexer</span> <span class="bp">self</span><span class="o">.</span><span class="n">_coroutines</span> <span class="o">=</span> <span class="p">[]</span> <span class="k">def</span> <span class="nf">register_handler</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">event_handler</span><span class="p">,</span> <span class="n">event_type</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">synchronous_event_demultiplexer</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">event_handler</span><span class="o">.</span><span class="n">get_handle</span><span class="p">(),</span> <span class="n">event_type</span><span class="p">,</span> <span class="n">event_handler</span><span class="p">)</span> <span class="k">def</span> <span class="nf">remove_handler</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">event_handler</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">synchronous_event_demultiplexer</span><span class="o">.</span><span class="n">unregister</span><span class="p">(</span><span class="n">event_handler</span><span class="o">.</span><span class="n">get_handle</span><span class="p">())</span> <span class="k">def</span> <span class="nf">add_coroutine</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">coroutine</span><span class="p">):</span> <span class="k">try</span><span class="p">:</span> <span class="n">coroutine</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="bp">None</span><span class="p">)</span> <span class="k">except</span> <span class="ne">StopIteration</span><span class="p">:</span> <span class="k">pass</span> <span class="k">else</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">_coroutines</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">coroutine</span><span class="p">)</span> <span class="k">def</span> <span class="nf">handle_events</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">handles</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">synchronous_event_demultiplexer</span><span class="o">.</span><span class="n">select</span><span class="p">()</span> <span class="k">for</span> <span class="n">handle</span><span class="p">,</span> <span class="n">event</span> <span class="ow">in</span> <span class="n">handles</span><span class="p">:</span> <span class="n">handle</span><span class="o">.</span><span class="n">data</span><span class="o">.</span><span class="n">handle_event</span><span class="p">()</span> <span class="n">not_executed</span> <span class="o">=</span> <span class="p">[]</span> <span class="k">for</span> <span class="n">coroutine</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_coroutines</span><span class="p">:</span> <span class="k">try</span><span class="p">:</span> <span class="n">coroutine</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="bp">None</span><span class="p">)</span> <span class="k">except</span> <span class="ne">StopIteration</span><span class="p">:</span> <span class="k">pass</span> <span class="k">else</span><span class="p">:</span> <span class="n">not_executed</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">coroutine</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">_coroutines</span> <span class="o">=</span> <span class="n">not_executed</span> </pre> </div> </div> <div class="tab-pane" id="code-8"> <div class="highlight"> <pre class="literal-block"> <span class="kn">from</span> <span class="nn">reactor</span> <span class="k">import</span> <span class="n">EventHandler</span><span class="p">,</span> <span class="n">SynchronousEventDemultiplexer</span><span class="p">,</span> <span class="n">InitiationDispatcher</span> <span class="kn">from</span> <span class="nn">selectors</span> <span class="k">import</span> <span class="n">EVENT_READ</span> <span class="kn">from</span> <span class="nn">handles</span> <span class="k">import</span> <span class="n">FifoHandle</span> <span class="k">class</span> <span class="nc">PrintFifoEventHandler</span><span class="p">(</span><span class="n">EventHandler</span><span class="p">):</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">path_to_fifo</span><span class="p">):</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">FifoHandle</span><span class="p">(</span><span class="n">path_to_fifo</span><span class="p">))</span> <span class="k">def</span> <span class="nf">handle_event</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">type_event</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">handle</span><span class="o">.</span><span class="n">read</span><span class="p">())</span> <span class="k">class</span> <span class="nc">FifoReader</span><span class="p">:</span> <span class="k">class</span> <span class="nc">FifoReaderIterator</span><span class="p">(</span><span class="n">EventHandler</span><span class="p">):</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">path_to_fifo</span><span class="p">,</span> <span class="n">initiation_dispatcher</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">handle</span> <span class="o">=</span> <span class="n">FifoHandle</span><span class="p">(</span><span class="n">path_to_fifo</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">_initiation_dispatcher</span> <span class="o">=</span> <span class="n">initiation_dispatcher</span> <span class="bp">self</span><span class="o">.</span><span class="n">_ready</span> <span class="o">=</span> <span class="kc">False</span> <span class="bp">self</span><span class="o">.</span><span class="n">_data</span> <span class="o">=</span> <span class="kc">None</span> <span class="bp">self</span><span class="o">.</span><span class="n">_initiation_dispatcher</span><span class="o">.</span><span class="n">register_handler</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">EVENT_READ</span><span class="p">)</span> <span class="k">def</span> <span class="nf">__next__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_ready</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">StopIteration</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_data</span><span class="p">)</span> <span class="k">return</span> <span class="kc">None</span> <span class="k">def</span> <span class="nf">handle_event</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_ready</span> <span class="o">=</span> <span class="kc">True</span> <span class="bp">self</span><span class="o">.</span><span class="n">_data</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">handle</span><span class="o">.</span><span class="n">read</span><span class="p">()</span> <span class="bp">self</span><span class="o">.</span><span class="n">_initiation_dispatcher</span><span class="o">.</span><span class="n">remove_handler</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">path_to_fifo</span><span class="p">,</span> <span class="n">initiation_dispatcher</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_iterator</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">FifoReaderIterator</span><span class="p">(</span><span class="n">path_to_fifo</span><span class="p">,</span> <span class="n">initiation_dispatcher</span><span class="p">)</span> <span class="k">def</span> <span class="nf">__await__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_iterator</span> <span class="k">async</span> <span class="k">def</span> <span class="nf">concat</span><span class="p">():</span> <span class="n">a</span> <span class="o">=</span> <span class="k">await</span> <span class="n">FifoReader</span><span class="p">(</span><span class="s1">'/tmp/my_fifo_1'</span><span class="p">,</span> <span class="n">initiation_dispatcher</span><span class="p">)</span> <span class="n">b</span> <span class="o">=</span> <span class="k">await</span> <span class="n">FifoReader</span><span class="p">(</span><span class="s1">'/tmp/my_fifo_2'</span><span class="p">,</span> <span class="n">initiation_dispatcher</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="n">a</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="o">+</span> <span class="n">b</span><span class="o">.</span><span class="n">strip</span><span class="p">())</span> <span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span> <span class="n">synchronous_event_demultiplexer</span> <span class="o">=</span> <span class="n">SynchronousEventDemultiplexer</span><span class="p">()</span> <span class="n">initiation_dispatcher</span> <span class="o">=</span> <span class="n">InitiationDispatcher</span><span class="p">(</span><span class="n">synchronous_event_demultiplexer</span><span class="p">)</span> <span class="n">initiation_dispatcher</span><span class="o">.</span><span class="n">add_coroutine</span><span class="p">(</span><span class="n">concat</span><span class="p">())</span> <span class="n">fifo_event</span> <span class="o">=</span> <span class="n">PrintFifoEventHandler</span><span class="p">(</span><span class="s1">'/tmp/my_fifo'</span><span class="p">)</span> <span class="n">initiation_dispatcher</span><span class="o">.</span><span class="n">register_handler</span><span class="p">(</span><span class="n">fifo_event</span><span class="p">,</span> <span class="n">EVENT_READ</span><span class="p">)</span> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> <span class="n">initiation_dispatcher</span><span class="o">.</span><span class="n">handle_events</span><span class="p">()</span> </pre> </div> </div> </div> <p>Créez deux FIFO via la commande mkfifo:</p> <p><kbd class="kbd"> mkfifo /tmp/my_fifo_1 /tmp/my_fifo_2</kbd> </p> <p>Après avoir lancé le main.py</p> <p><kbd class="kbd"> python3.5 main.py</kbd> </p> <p>On peut utiliser un autre terminal pour écrire dans les FIFO.</p> <p><kbd class="kbd"> echo 12 &gt; /tmp/my_fifo_1</kbd> </p> <p><kbd class="kbd"> echo toto &gt; /tmp/my_fifo</kbd> </p> <p><kbd class="kbd"> echo 34 &gt; /tmp/my_fifo_2</kbd> </p> </div> <div class="section" id="frollons-une-boucle-d-evenements"> <h3>Frôllons une boucle d'événements</h3> <p>Ce que l'on vient de développer est très proche d'une boucle d'évènement telle que dans le framework AsyncIO ou Tornado Il faudrait que l'InitiationDispatcher soit encapsulé dans un singleton, pour qu'il puisse être appelé facilement dans plusieurs modules et également que l'on puisse gérer l'exécution de fonctions à un moment donnés. Pour implémenter cette fonctionnalité, on va utiliser le paramètre, optionnel, timeout de select qui permet de dire à select, bloque tant qu'un fichier n'est pas près à être lu ou que le temps donné par timeout est écoulé.</p> <p>On va ajouter une méthode <strong>call_later</strong> à notre InitiationDispatcher pour stoker les fonctions à exécuter ainsi que le moment auquel il doit les exécuter dans un tas. Les fonctions qui doivent être exécutée en première seront au sommet du tas. La méthode <strong>handle_events</strong> va regarder s'il y a une fonction en attente au sommet du tas et récupérer le moment auquel elle doit l'exécute. Elle va utiliser ce moment pour initialiser le paramètre timeout de select. Lorsque select rend la main, on ne sais pas si c'est à cause d'un timeout, d'un handler qui est près ou les deux. Il faut vérifier s'il est temps d'exécuter la fonction au sommet du tas puis la retirer de ce tas si c'est le cas.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-9">extrait de reactor.py </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-9"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">InitiationDispatcher</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">synchronous_event_demultiplexer</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">synchronous_event_demultiplexer</span> <span class="o">=</span> <span class="n">synchronous_event_demultiplexer</span> <span class="bp">self</span><span class="o">.</span><span class="n">_coroutines</span> <span class="o">=</span> <span class="p">[]</span> <span class="bp">self</span><span class="o">.</span><span class="n">_heap</span> <span class="o">=</span> <span class="p">[]</span> <span class="k">def</span> <span class="nf">register_handler</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">event_handler</span><span class="p">,</span> <span class="n">event_type</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">synchronous_event_demultiplexer</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">event_handler</span><span class="o">.</span><span class="n">get_handle</span><span class="p">(),</span> <span class="n">event_type</span><span class="p">,</span> <span class="n">event_handler</span><span class="p">)</span> <span class="k">def</span> <span class="nf">remove_handler</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">event_handler</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">synchronous_event_demultiplexer</span><span class="o">.</span><span class="n">unregister</span><span class="p">(</span><span class="n">event_handler</span><span class="o">.</span><span class="n">get_handle</span><span class="p">())</span> <span class="k">def</span> <span class="nf">add_coroutine</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">coroutine</span><span class="p">):</span> <span class="k">try</span><span class="p">:</span> <span class="n">coroutine</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="kc">None</span><span class="p">)</span> <span class="k">except</span> <span class="ne">StopIteration</span><span class="p">:</span> <span class="k">pass</span> <span class="k">else</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">_coroutines</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">coroutine</span><span class="p">)</span> <span class="k">def</span> <span class="nf">call_later</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">delay</span><span class="p">,</span> <span class="n">func</span><span class="p">):</span> <span class="n">heappush</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_heap</span><span class="p">,</span> <span class="p">[</span><span class="n">time</span><span class="p">()</span> <span class="o">+</span> <span class="n">delay</span><span class="p">,</span> <span class="n">func</span><span class="p">])</span> <span class="k">def</span> <span class="nf">handle_events</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_heap</span><span class="p">:</span> <span class="n">moment</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_heap</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="n">timeout</span> <span class="o">=</span> <span class="n">moment</span> <span class="o">-</span> <span class="n">time</span><span class="p">()</span> <span class="k">else</span><span class="p">:</span> <span class="n">moment</span> <span class="o">=</span> <span class="kc">None</span> <span class="n">timeout</span> <span class="o">=</span> <span class="kc">None</span> <span class="n">handles</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">synchronous_event_demultiplexer</span><span class="o">.</span><span class="n">select</span><span class="p">(</span><span class="n">timeout</span><span class="p">)</span> <span class="k">for</span> <span class="n">handle</span><span class="p">,</span> <span class="n">event</span> <span class="ow">in</span> <span class="n">handles</span><span class="p">:</span> <span class="n">handle</span><span class="o">.</span><span class="n">data</span><span class="o">.</span><span class="n">handle_event</span><span class="p">()</span> <span class="k">if</span> <span class="n">moment</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">moment</span> <span class="o">&lt;=</span> <span class="n">time</span><span class="p">():</span> <span class="n">heappop</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_heap</span><span class="p">)[</span><span class="mi">1</span><span class="p">]()</span> <span class="n">not_executed</span> <span class="o">=</span> <span class="p">[]</span> <span class="k">for</span> <span class="n">coroutine</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_coroutines</span><span class="p">:</span> <span class="k">try</span><span class="p">:</span> <span class="n">coroutine</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="kc">None</span><span class="p">)</span> <span class="k">except</span> <span class="ne">StopIteration</span><span class="p">:</span> <span class="k">pass</span> <span class="k">else</span><span class="p">:</span> <span class="n">not_executed</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">coroutine</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">_coroutines</span> <span class="o">=</span> <span class="n">not_executed</span> </pre> </div> </div> </div> <p>Voilà, j'espère que ça vous a plu, et que les boucles d'évènement vous paraîtront un peu moins magiques. <img alt="laughing" src="/theme/img/smiley/smiley-laughing.gif" /></p> <p>A vos IDE et bon code.</p> </div> </div> L'histoire du MVC (Modèle, Vue Controller)2016-09-29T15:24:00+02:002016-09-29T15:24:00+02:00Vincent Mailloltag:autourducode.com,2016-09-29:/histoire-du-mvc.html<p class="first last">L'histoire du MVC de 1978 à son adaptation dans le web.</p> <p>Nous allons partir en 1978 pour assister à la naissance du <strong>MVC</strong>...</p> <p>Le MVC (Model, View, Controller) traduit par Modèle, Vue, Contrôleur en français est un modèle d'architecture utisé pour la première fois dans le langage de programmation <strong>Smalltalk</strong>.</p> <p>C'est dans un article de 1988 intitulé &quot;A Cookbook for Using the Model-View-Controller User Interface Paradigm in Smalltalk -80&quot; que le principe de programmation MVC est présenté au public. Cette publication fait suite aux travaux de 1978 de Trygve Reenskaug sur le <a class="reference external" href="https://heim.ifi.uio.no/~trygver/1979/mvc-2/1979-12-MVC.pdf">Model View Controller Editor</a> en 1979.</p> <p><strong>Smalltalk</strong> à l'instar de <strong>Python</strong> est un langage où tout est objet, c'est donc à partir de l'article de 1988 que nous allons implémenter notre architecture MVC en python. Commençons par le <strong>Modèle</strong>, cette partie dit contenir les informations métier.</p> <p>Le modèle doit avoir des composants qui lui sont dépendants, les vues, et il doit pouvoir envoyer un même message à toutes ses vues en une seule fois. L'expression envoie de message peut vous sembler obscure, en fait dans la littérature sur la programmation orienté objet, on parle d'envoi de message pour parler d'appel de méthode. Dans notre implémentation, on utilisera la méthode <strong>changed</strong> qui appellera la méthode <strong>update</strong> des vues.</p> <div class="section" id="le-modele"> <h2>Le Modèle</h2> <p><strong>Le Modèle</strong> à la responsabilité d'ajouter ses <strong>Vues</strong> à lui même. En fait la relation entre le Modèle et la Vue est un patron de conception <a class="reference external" href="http://autourducode.com/le-design-pattern-observer-et-ses-variantes.html">Observateur</a>. Le modèle est un Observable et les vues sont des Observateurs</p> </div> <div class="section" id="la-vue"> <h2>La Vue</h2> <p>La vue a pour responsabilité l'affichage des données d'un modèle. Les vues ont un contrôleur dédié. La classe du contrôleur d'une vue est accessible via l'attribut <strong>default_controller_class</strong>. Le modèle est passé explicitement à la vue, par le constructeur. Lors de l'instanciation d'une vue, elle instancie son contrôleur dédié auquel elle passe le modèle</p> <p>Les vues sont conçue pour être imbriquées les unes aux autres. Chaque vue peut contenir plusieurs sous-vues et ont une référence vers leur vue parente. Elle possède une méthode update pour se rafraîchir l'appel de cette méthode remet à jours les sous-vues</p> </div> <div class="section" id="le-controleur"> <h2>Le Contrôleur</h2> <p>Il gère les évènements utilisateurs est manipule le modèle. C'est ce qui lie la vue au modèle.</p> <p>Dans notre exemple, on va simplifier pour mettre en avant les avantages du MVC, de ce fait, on va faire plusieurs modifications - Le contrôleur ne récupérera pas réellement des événements, mais lira l'entré standard. - Les Vues ne seront pas imbriquable, de toute façon, il y en aura qu'une seule.</p> <p>Le fichier <strong>mvc.py</strong> définis les trois classes de base et le fichier <strong>mvc_exemple.py</strong> hérite de ces classes pour mettre en place un compteur. Le modèle du compteur est garant de la logique, c'est-à-dire que le conteur peut être incrémenté d'un à un et lorsque l'on est à 9, il retourne à 0.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">mvc.py </a></li> <li><a class="reference internal" href="#code-2">mvc_exemple.py </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Model</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">views</span> <span class="o">=</span> <span class="p">[]</span> <span class="k">def</span> <span class="nf">_changed</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="k">for</span> <span class="n">view</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">views</span><span class="p">:</span> <span class="n">view</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="k">class</span> <span class="nc">Controller</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">model</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">model</span> <span class="o">=</span> <span class="n">model</span> <span class="k">def</span> <span class="nf">listen_user_event</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="o">...</span> <span class="k">class</span> <span class="nc">View</span><span class="p">:</span> <span class="n">default_controller_class</span> <span class="o">=</span> <span class="n">Controller</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">model</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">model</span> <span class="o">=</span> <span class="n">model</span> <span class="n">model</span><span class="o">.</span><span class="n">views</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">controller</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">default_controller_class</span><span class="p">(</span><span class="n">model</span><span class="p">)</span> <span class="k">def</span> <span class="nf">update</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="o">...</span> </pre> </div> </div> <div class="tab-pane" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="kn">from</span> <span class="nn">mvc</span> <span class="k">import</span> <span class="n">Model</span><span class="p">,</span> <span class="n">Controller</span><span class="p">,</span> <span class="n">View</span> <span class="k">class</span> <span class="nc">CounterModel</span><span class="p">(</span><span class="n">Model</span><span class="p">):</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">()</span> <span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">def</span> <span class="nf">incr</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">&gt;</span> <span class="mi">9</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="mi">0</span> <span class="bp">self</span><span class="o">.</span><span class="n">_changed</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">value</span><span class="p">)</span> <span class="k">class</span> <span class="nc">CounterController</span><span class="p">(</span><span class="n">Controller</span><span class="p">):</span> <span class="k">def</span> <span class="nf">listen_user_event</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">value</span> <span class="o">=</span> <span class="mi">1</span> <span class="k">while</span> <span class="n">value</span><span class="p">:</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">value</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">model</span><span class="o">.</span><span class="n">incr</span><span class="p">()</span> <span class="n">value</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="nb">input</span><span class="p">(</span><span class="s1">'Ajouter un chiffre au compteur : '</span><span class="p">))</span> <span class="k">class</span> <span class="nc">CounterView</span><span class="p">(</span><span class="n">View</span><span class="p">):</span> <span class="n">default_controller_class</span> <span class="o">=</span> <span class="n">CounterController</span> <span class="n">digits</span> <span class="o">=</span> <span class="p">(</span><span class="s2">&quot;zero&quot;</span><span class="p">,</span> <span class="s2">&quot;un&quot;</span><span class="p">,</span> <span class="s2">&quot;deux&quot;</span><span class="p">,</span> <span class="s2">&quot;trois&quot;</span><span class="p">,</span> <span class="s2">&quot;quatre&quot;</span><span class="p">,</span> <span class="s2">&quot;cinq&quot;</span><span class="p">,</span> <span class="s2">&quot;six&quot;</span><span class="p">,</span> <span class="s2">&quot;sept&quot;</span><span class="p">,</span> <span class="s2">&quot;huit&quot;</span><span class="p">,</span> <span class="s2">&quot;neuf&quot;</span><span class="p">)</span> <span class="k">def</span> <span class="nf">update</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Compteur:&quot;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">digits</span><span class="p">[</span><span class="n">value</span><span class="p">])</span> <span class="n">model</span> <span class="o">=</span> <span class="n">CounterModel</span><span class="p">()</span> <span class="n">view</span> <span class="o">=</span> <span class="n">CounterView</span><span class="p">(</span><span class="n">model</span><span class="p">)</span> <span class="n">view</span><span class="o">.</span><span class="n">controller</span><span class="o">.</span><span class="n">listen_user_event</span><span class="p">()</span> </pre> </div> </div> </div> <p>Le gros avantage, du MVC, c'est que si demain, je dois afficher la vue autrement, cela ne remettra pas en question les autres composant. Si je change de machine pour une machine qui n'a pas de touche numérique, je peux changer le contrôleur pour qu'il modifie le modèle lorsque la touche haut est enfoncée. Cela ne remettra pas en question les autres composant. De plus, j'ai la possibilité de faire écouter un même modèle par plusieurs vues comme ceci <img alt="cool" src="/theme/img/smiley/smiley-cool.gif" /></p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-3">mvc_exemple.py </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="n">model</span> <span class="o">=</span> <span class="n">CounterModel</span><span class="p">()</span> <span class="n">view_1</span> <span class="o">=</span> <span class="n">CounterView</span><span class="p">(</span><span class="n">model</span><span class="p">)</span> <span class="n">view_2</span> <span class="o">=</span> <span class="n">CounterView</span><span class="p">(</span><span class="n">model</span><span class="p">)</span> <span class="n">view_2</span><span class="o">.</span><span class="n">digits</span> <span class="o">=</span> <span class="p">(</span><span class="s2">&quot;null&quot;</span><span class="p">,</span> <span class="s2">&quot;ein&quot;</span><span class="p">,</span> <span class="s2">&quot;zwei&quot;</span><span class="p">,</span> <span class="s2">&quot;drei&quot;</span><span class="p">,</span> <span class="s2">&quot;vier&quot;</span><span class="p">,</span> <span class="s2">&quot;fünf&quot;</span><span class="p">,</span> <span class="s2">&quot;sechs&quot;</span><span class="p">,</span> <span class="s2">&quot;sieben&quot;</span><span class="p">,</span> <span class="s2">&quot;acht&quot;</span><span class="p">,</span> <span class="s2">&quot;neun&quot;</span><span class="p">)</span> <span class="n">view_2</span><span class="o">.</span><span class="n">controller</span> <span class="o">=</span> <span class="n">view_1</span><span class="o">.</span><span class="n">controller</span> <span class="n">view_1</span><span class="o">.</span><span class="n">controller</span><span class="o">.</span><span class="n">listen_user_event</span><span class="p">()</span> </pre> </div> </div> </div> <p>Si le modèle change, cela peut impacter la vue et le contrôleur, mais cela doit être vu comme une fatalité et non un problème qui doit être résolu. En effet si le modèle doit changer, cela veut dire que le logique métier a changé ce n'est donc pas choquant que tout puisse changer. Par analogie, sur un grille-pain, je dois pouvoir changer le design sans changer le mécanisme. Par contre si demain, je fais des gaufres à la place du pain griller, tout change.</p> <p>Bon, il est temps d'avancer un peu dans le temps ! <img alt="laughing" src="/theme/img/smiley/smiley-laughing.gif" /></p> </div> <div class="section" id="le-web-et-le-mvc"> <h2>Le web et le MVC</h2> <p>Les années passent et le web se développe d'abord statique puis dynamique, on a de plus en plus d'interactions entre les données coté serveur et les pages affichées par le navigateur. Il faut mettre en place la stratégie MVC dans le web.</p> <p>Si l'on regarde rapidement, les vues seront les pages HTML affichées par le navigateur. Elles génèrent des évènements via l'envoi de requêtes HTTP POST et GET lorsque l'utilisateur clique sur des liens ou envoie des formulaires. Côté serveur, on aura les contrôleurs et les modèles.</p> <p>Sauf que quand on regarde de plus près, un problème se pose, on voie mal comment le modèle va notifier la vue d'un changement, c'est dû à la nature du protocole HTTP. Le serveur web ne peut pas envoyer une Requête au navigateur pour lui dire de rafraîchir la vue. C'est le navigateur qui doit demander.</p> </div> <div class="section" id="sun-sort-le-mvc-modele-2"> <h2>Sun sort le MVC modèle 2</h2> <p>Le monde Java est le premier à tenter l'adaptation sur des serveurs d'applications. Les serveurs d'applications sont chargés de recevoir des requêtes HTTP et de fournir ces requêtes en fonction de l'URL demandée à une classe Servlet. Les JSP sortie en 1998 sont des gabaris dans lesquelles on définie des pages HTML dans lequel ce glisse des instructions en Java c'est une technologie parfaite pour faire les vues. Les JSP sont ensuite compilées en Servlet.</p> <p>Sun propose deux types d'architecture <strong>le modèle 1</strong> et <strong>le modèle 2</strong> <img alt="undecided" src="/theme/img/smiley/smiley-undecided.gif" /></p> <p>Dans la première, une JSP fait office de contrôleur en étant lié à une URL. La JSP va chercher elle-même ces données pour s'afficher auprès du modèle.</p> <p>Dans la seconde, il n'y a qu'un seul contrôleur, qui est implémente par un servelet. Ce contrôleur va instancier le modèle. JSP est utilisé pour afficher les modèles que le contrôleur fournit. Le contrôleur à aussi la charge de sélectionner la bonne JSP en fonction de la requête reçue.</p> <p>Le gros avantage du modèle 2, c'est qu'il allège les vues de le logique métier en les rendants moins dépendantes du modèle. Il permet aussi de centraliser les URL.</p> <p>Ainsi, le modèle 1 sera également connu sous le nom de <strong>Component-based</strong> ou <strong>pull-based</strong> et le modèle 2 sous le nom de <strong>push-based</strong> ou <strong>Action-base</strong>, on verra également apparaitre le terme MVC2 dû à l'abréviation de MVC modèle 2. Voilà pourquoi on parle de MVC pour les frameworks web qui ont une architecture modèle 2, mais comme on peut le voir, ce n'est plus vraiment du MVC.</p> <p>Dans le futur, les WebSocket pourraient changer la donne, en effet comme elle permette une communication asynchrone entre le serveur et le client, la vue pourrait de nouveau être notifiée par le modèle en cas de changement. <img alt="wink" src="/theme/img/smiley/smiley-wink.gif" /></p> <p>Sur ce, si vous voulez en savoir plus, je vous laisse les clés de la Delorean pour consulter cet <a class="reference external" href="http://ptgmedia.pearsoncmg.com/imprint_downloads/informit/chap2_0672324725.pdf">article d'InformIT</a></p> <p>A vos IDE et bon code <img alt="smile" src="/theme/img/smiley/smiley-smile.gif" /></p> </div> Utiliser une API REST avec Angular2 et le module HTTP2016-09-23T19:24:00+02:002016-09-23T19:24:00+02:00Vincent Mailloltag:autourducode.com,2016-09-23:/angular2-sur-api-rest-avec-le-module-http.html<p class="first last">Faire communiquer une application angular2 avec une API ReST</p> <p>Nous allons reprendre l'application angular2 que l'on avait développé dans <a class="reference external" href="http://autourducode.com/angular2-le-tutoriel-interaction-entre-composant.html">le précédent tutoriel</a>. Actuellement les membres de l'équipe affichés par l'application sont codés en dur dans la classe StargateCommand. On va modifier notre application pour aller chercher les équipiers sur un serveur via une <strong>API ReST</strong>.</p> <p>Dans le répertoire du projet nommé sg1, on va lancer la commande suivante pour installer <strong>le module http d'angular 2</strong></p> <p><kbd class="kbd"> npm install --save &#64;angular/http&#64;2.0.0</kbd> </p> <p>Puis on va mettre à jour notre <strong>app.module</strong> pour importer le module http dans l'application et mettre à jour la configuration de <strong>systemjs</strong> pour que notre navigateur puisse charger le module http d'angular 2.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">sg1/app/app.module.ts </a></li> <li><a class="reference internal" href="#code-2">sg1/app/systemjs.config.ts </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="kr">import</span> <span class="p">{</span> <span class="nx">NgModule</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">'&#64;angular/core'</span><span class="p">;</span> <span class="kr">import</span> <span class="p">{</span> <span class="nx">BrowserModule</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">'&#64;angular/platform-browser'</span><span class="p">;</span> <span class="kr">import</span> <span class="p">{</span> <span class="nx">HttpModule</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">'&#64;angular/http'</span><span class="p">;</span> <span class="kr">import</span> <span class="p">{</span> <span class="nx">StargateCommand</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">'./app.sgc'</span><span class="p">;</span> <span class="kr">import</span> <span class="p">{</span> <span class="nx">TeamMember</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">'./app.teammember'</span><span class="p">;</span> <span class="kd">&#64;NgModule</span><span class="p">({</span> <span class="nx">imports</span><span class="o">:</span> <span class="p">[</span> <span class="nx">BrowserModule</span><span class="p">,</span> <span class="nx">HttpModule</span> <span class="p">],</span> <span class="nx">declarations</span><span class="o">:</span> <span class="p">[</span> <span class="nx">StargateCommand</span><span class="p">,</span> <span class="nx">TeamMember</span> <span class="p">],</span> <span class="nx">bootstrap</span><span class="o">:</span> <span class="p">[</span> <span class="nx">StargateCommand</span> <span class="p">]</span> <span class="p">})</span> <span class="kr">export</span> <span class="kr">class</span> <span class="nx">AppModule</span> <span class="p">{</span> <span class="p">}</span> </pre> </div> </div> <div class="tab-pane" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">global</span><span class="p">)</span> <span class="p">{</span> <span class="nx">System</span><span class="p">.</span><span class="nx">config</span><span class="p">({</span> <span class="cm">/* Cette map permet d'indiquer quelle fichier il faut chargé * en fonction du nom de module importé. */</span> <span class="nx">map</span><span class="o">:</span> <span class="p">{</span> <span class="s1">'app'</span><span class="o">:</span> <span class="s1">'app'</span><span class="p">,</span> <span class="s1">'&#64;angular/core'</span><span class="o">:</span> <span class="s1">'node_modules/&#64;angular/core/bundles/core.umd.js'</span><span class="p">,</span> <span class="s1">'&#64;angular/common'</span><span class="o">:</span> <span class="s1">'node_modules/&#64;angular/common/bundles/common.umd.js'</span><span class="p">,</span> <span class="s1">'&#64;angular/compiler'</span><span class="o">:</span> <span class="s1">'node_modules/&#64;angular/compiler/bundles/compiler.umd.js'</span><span class="p">,</span> <span class="s1">'&#64;angular/platform-browser'</span><span class="o">:</span> <span class="s1">'node_modules/&#64;angular/platform-browser/bundles/platform-browser.umd.js'</span><span class="p">,</span> <span class="s1">'&#64;angular/platform-browser-dynamic'</span><span class="o">:</span> <span class="s1">'node_modules/&#64;angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js'</span><span class="p">,</span> <span class="s1">'&#64;angular/http'</span><span class="o">:</span> <span class="s1">'node_modules/&#64;angular/http/bundles/http.umd.js'</span><span class="p">,</span> <span class="s1">'rxjs'</span><span class="o">:</span> <span class="s1">'node_modules/rxjs'</span> <span class="p">},</span> <span class="cm">/* Le point d'entrée du module app est main.js et * l'extension des fichiers est .js. */</span> <span class="nx">packages</span><span class="o">:</span> <span class="p">{</span> <span class="nx">app</span><span class="o">:</span> <span class="p">{</span> <span class="nx">main</span><span class="o">:</span> <span class="s1">'./main.js'</span><span class="p">,</span> <span class="nx">defaultExtension</span><span class="o">:</span> <span class="s1">'js'</span> <span class="p">},</span> <span class="nx">rxjs</span><span class="o">:</span> <span class="p">{</span> <span class="nx">defaultExtension</span><span class="o">:</span> <span class="s1">'js'</span> <span class="p">}</span> <span class="p">}</span> <span class="p">});</span> <span class="p">})(</span><span class="k">this</span><span class="p">);</span> </pre> </div> </div> </div> <div class="section" id="mettre-en-place-un-service-rest"> <h2>Mettre en place un service ReST</h2> <p>Pour rapidement mettre en place un service ReST on va utiliser Woof, Woof est un framework qui permet de créer une API REsT sur une base de données. Comme il est en cours de développement, on va récupérer Woof et basculer sur numéro de commit précis pour ne pas avoir trop de surprise.</p> <p><kbd class="kbd"> git clone https://github.com/Maillol/woof.git</kbd> </p> <p><kbd class="kbd"> cd woof</kbd> </p> <p><kbd class="kbd"> git reset --hard 99dbdc1960f509397fe8e15a8ba63e0ce53c17ac</kbd> </p> <p>Après téléchargement, l'installation du framework ce fait comme ceci:</p> <p><kbd class="kbd"> cd woof</kbd> </p> <p><kbd class="kbd"> python3 setup.py install</kbd> </p> <p>Dans le répertoire contenant le répertoire sg1, on va créer notre application <strong>sgc</strong> via la commande</p> <p><kbd class="kbd"> woof startproject sgc</kbd> </p> <p>On va se retrouver avec un répertoire sgc au côté du répertoire sg1.</p> <p>Puis créer notre ressource TeamMember pour stoker les données et lier notre ressource à l'URL /api/team-member.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-3">sgc/sgc/models.py </a></li> <li><a class="reference internal" href="#code-4">sgc/sgc/controllers.py </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="kn">from</span> <span class="nn">woof.resource</span> <span class="k">import</span> <span class="n">Resource</span><span class="p">,</span> <span class="n">IntegerField</span><span class="p">,</span> <span class="n">StringField</span> <span class="k">class</span> <span class="nc">TeamMember</span><span class="p">(</span><span class="n">Resource</span><span class="p">):</span> <span class="n">name</span> <span class="o">=</span> <span class="n">StringField</span><span class="p">()</span> <span class="n">health</span> <span class="o">=</span> <span class="n">IntegerField</span><span class="p">()</span> </pre> </div> </div> <div class="tab-pane" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="ch">#!/usr/bin/env python3</span> <span class="kn">from</span> <span class="nn">woof.url</span> <span class="k">import</span> <span class="n">EntryPoint</span> <span class="kn">from</span> <span class="nn">.models</span> <span class="k">import</span> <span class="n">TeamMember</span> <span class="n">root_url</span> <span class="o">=</span> <span class="n">EntryPoint</span><span class="p">(</span><span class="s1">'/api'</span><span class="p">)</span> <span class="n">root_url</span><span class="o">.</span><span class="n">crud</span><span class="p">(</span><span class="s1">'team-member/[id]'</span> <span class="n">TeamMember</span><span class="p">)</span> </pre> </div> </div> </div> <p>On demande au framework de créer la base de données de l'application sgc.</p> <p><kbd class="kbd"> woof createdb --py-path sgc --conf sgc/config.json sgc</kbd> </p> <p>Woof est livré avec <strong>un mini serveur de développement</strong> qui peut également servir des contenus statiques. On va donc l'utiliser pour lancer notre service reste mais également servir notre <strong>index.html</strong> et l'application Angular 2.</p> <p><kbd class="kbd"> woof runserver --static-dir sg1 sgc --port 8000</kbd> </p> <p>On va utiliser l'utilitaire <strong>curl</strong> pour insérer quelques données dans la base</p> <p><kbd class="kbd"> curl -X POST http://127.0.0.1:8000/api/team-member -d '{&quot;name&quot;: &quot;Jack O''neal&quot;, &quot;health&quot;: 10}'</kbd> </p> <p><kbd class="kbd"> curl -X POST http://127.0.0.1:8000/api/team-member -d '{&quot;name&quot;: &quot;Daniel Jackson&quot;, &quot;health&quot;: 4}'</kbd> </p> <p><kbd class="kbd"> curl -X POST http://127.0.0.1:8000/api/team-member -d '{&quot;name&quot;: &quot;Samantha Carter&quot;, &quot;health&quot;: 10}'</kbd> </p> <p><kbd class="kbd"> curl -X POST http://127.0.0.1:8000/api/team-member -d '{&quot;name&quot;: &quot;Teal''c&quot;, &quot;health&quot;: 10}'</kbd> </p> </div> <div class="section" id="creation-d-un-service-pour-dialoguer-avec-le-serveur"> <h2>Création d'un service pour dialoguer avec le serveur</h2> <p>On va créer une classe <strong>TeamMemberService</strong> qui va nous permettre de récupérer les membres des équipes au serveur. Cette classe est décorée avec <strong>Injectable</strong>. Elle possède une méthode getTeamMembers qui utilise <strong>http.get</strong> pour récupère les données à l'URL api/team-member http.get va renvoyer <a class="reference external" href="https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/observable.md#observable-object">un objet Observable</a> de la librairie <strong>RxJS</strong> sur lequel on applique la méthode map en passant une callback qui transformera nos données en JSON. On va également écrire la méthode addTeamMember qui envoie un membre d'équipe au format JSON pour que l'application Woof l'enregistre.</p> <p>On va ensuite utiliser notre service dans notre classe <strong>StargateCommand</strong> en ajoutant un constructeur qui va utiliser le service pour mettre à jour la liste. Angular 2 va fournir automatiquement une instance de TeamMemberService lors de l'instanciation de StargateCommand. On doit cependant déclarer TeamMemberService dans le tableau providers du décorateur Component.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-5">app/app.teammemberservice.ts </a></li> <li><a class="reference internal" href="#code-6">app/app.sgc.ts </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-5"> <div class="highlight"> <pre class="literal-block"> <span class="kr">import</span> <span class="p">{</span> <span class="nx">Injectable</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">'&#64;angular/core'</span><span class="p">;</span> <span class="kr">import</span> <span class="p">{</span> <span class="nx">Http</span><span class="p">,</span> <span class="nx">Headers</span><span class="p">,</span> <span class="nx">RequestOptions</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">'&#64;angular/http'</span><span class="p">;</span> <span class="kr">import</span> <span class="s1">'rxjs/add/operator/map'</span><span class="p">;</span> <span class="kd">&#64;Injectable</span><span class="p">()</span> <span class="kr">export</span> <span class="kr">class</span> <span class="nx">TeamMemberService</span> <span class="p">{</span> <span class="kr">constructor</span><span class="p">(</span><span class="kr">private</span> <span class="nx">http</span>: <span class="kt">Http</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span> <span class="kr">private</span> <span class="nx">apiUrl</span> <span class="o">=</span> <span class="s1">'api/team-member'</span><span class="p">;</span> <span class="nx">getTeamMembers() {</span> <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">http</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">apiUrl</span><span class="p">)</span> <span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">response</span> <span class="o">=&gt;</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">());</span> <span class="p">}</span> <span class="nx">addTeamMember</span><span class="p">(</span><span class="nx">name</span>: <span class="kt">string</span><span class="p">,</span> <span class="nx">health</span>: <span class="kt">number</span><span class="p">)</span> <span class="p">{</span> <span class="kd">let</span> <span class="nx">body</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">({</span> <span class="nx">name</span>: <span class="kt">name</span><span class="p">,</span> <span class="nx">health</span>: <span class="kt">health</span> <span class="p">});</span> <span class="kd">let</span> <span class="nx">headers</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Headers</span><span class="p">({</span> <span class="s1">'Content-Type'</span><span class="o">:</span> <span class="s1">'application/json'</span> <span class="p">});</span> <span class="kd">let</span> <span class="nx">options</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">RequestOptions</span><span class="p">({</span> <span class="nx">headers</span>: <span class="kt">headers</span> <span class="p">});</span> <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">http</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">apiUrl</span><span class="p">,</span> <span class="nx">body</span><span class="p">,</span> <span class="nx">options</span><span class="p">)</span> <span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">data</span> <span class="o">=&gt;</span> <span class="nx">data</span><span class="p">.</span><span class="nx">json</span><span class="p">());</span> <span class="p">}</span> <span class="p">}</span> </pre> </div> </div> <div class="tab-pane" id="code-6"> <div class="highlight"> <pre class="literal-block"> <span class="kr">import</span> <span class="p">{</span> <span class="nx">Component</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">'&#64;angular/core'</span><span class="p">;</span> <span class="kr">import</span> <span class="p">{</span> <span class="nx">TeamMember</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">'./app.teammember'</span><span class="p">;</span> <span class="kr">import</span> <span class="p">{</span> <span class="nx">TeamMemberService</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">'./app.teammemberservice'</span><span class="p">;</span> <span class="kd">&#64;Component</span><span class="p">({</span> <span class="nx">selector</span><span class="o">:</span> <span class="s1">'sgc'</span><span class="p">,</span> <span class="nx">template</span><span class="o">:</span> <span class="sb">` &lt;p&gt;{{ message }}&lt;/p&gt; &lt;ul&gt; &lt;li *ngFor=&quot;let member of members&quot;&gt; &lt;team-member [name]=&quot;member.name&quot; [health]=&quot;member.health&quot; (missionDone)=&quot;onMissionDone($event)&quot;&gt;&lt;/team-member&gt; &lt;/li&gt; &lt;/ul&gt; &lt;input #nameMember&gt; &lt;button (click)=&quot;addMember(nameMember.value, 10)&quot;&gt;Add &lt;/button&gt; `</span><span class="p">,</span> <span class="nx">providers</span><span class="o">:</span> <span class="p">[</span><span class="nx">TeamMemberService</span><span class="p">]</span> <span class="p">})</span> <span class="kr">export</span> <span class="kr">class</span> <span class="nx">StargateCommand</span> <span class="p">{</span> <span class="nx">message</span>:<span class="kt">string</span><span class="p">;</span> <span class="nx">members</span>: <span class="kt">Array</span><span class="o">&lt;</span><span class="nx">any</span><span class="o">&gt;</span> <span class="o">=</span> <span class="p">[];</span> <span class="nx">teamMemberService</span>: <span class="kt">TeamMemberService</span><span class="p">;</span> <span class="kr">constructor</span><span class="p">(</span><span class="nx">teamMemberService</span>: <span class="kt">TeamMemberService</span><span class="p">)</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">teamMemberService</span> <span class="o">=</span> <span class="nx">teamMemberService</span><span class="p">;</span> <span class="k">this</span><span class="p">.</span><span class="nx">getMembers</span><span class="p">();</span> <span class="p">}</span> <span class="nx">getMembers() {</span> <span class="k">this</span><span class="p">.</span><span class="nx">teamMemberService</span><span class="p">.</span><span class="nx">getTeamMembers</span><span class="p">().</span><span class="nx">subscribe</span><span class="p">(</span> <span class="nx">teamMembers</span> <span class="o">=&gt;</span> <span class="k">this</span><span class="p">.</span><span class="nx">members</span> <span class="o">=</span> <span class="nx">teamMembers</span><span class="p">,</span> <span class="nx">error</span> <span class="o">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">);</span> <span class="p">}</span> <span class="nx">addMember</span><span class="p">(</span><span class="nx">name</span>: <span class="kt">string</span><span class="p">,</span> <span class="nx">health</span>: <span class="kt">number</span><span class="p">)</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">teamMemberService</span><span class="p">.</span><span class="nx">addTeamMember</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">health</span><span class="p">)</span> <span class="p">.</span><span class="nx">subscribe</span><span class="p">(</span><span class="nx">data</span> <span class="o">=&gt;</span> <span class="k">this</span><span class="p">.</span><span class="nx">members</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">data</span><span class="p">),</span> <span class="nx">error</span> <span class="o">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">error</span><span class="p">));</span> <span class="p">}</span> <span class="nx">onMissionDone</span><span class="p">(</span><span class="nx">teamMember</span><span class="p">)</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">message</span> <span class="o">=</span> <span class="nx">teamMember</span><span class="p">.</span><span class="nx">name</span> <span class="o">+</span> <span class="s2">&quot; had just returned from a mission&quot;</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> </pre> </div> </div> </div> <p>On va rapidement modifier le gabarit de StargateCommand pour créer un petit formulaire pour saisir le nom d'un nouvel équipier. Remarquez l'utilisation du symbole <strong>#</strong> pour déclarer les variables <strong>#healthMember</strong> et <strong>#nameMember</strong> et les lier à une balise input. Ces variables sont ensuite utilisées par la fonction <strong>addMember</strong> qui sera appelé sur un clic sur le bouton <strong>Add Team Member</strong>. Lorsque l'on appelle une méthode du <strong>teamMemberService</strong>, on appelle <strong>la méthode subscribe</strong> sur l'Observable retourné en lui passant deux fonctions. La première s'exécute en cas de succès et la seconde en cas d'erreur.</p> <p>Maintenant que nos modifications sont finie on va exécuter le compilateur TypesScript dans un autre terminal.</p> <p><kbd class="kbd"> cd sg1</kbd> </p> <p><kbd class="kbd"> tsc -w</kbd> </p> <p>Si vous n'avez pas stoppé votre serveur entre temps, le résultat est visible à l'addresse <a class="reference external" href="http://127.0.0.1:8000/">http://127.0.0.1:8000/</a></p> <p>Voilà, j'espère que ça vous a plu et que vous n'avez pas eu de difficultés. En attendant un prochain tuto, à vos IDE et bon code <img alt="laughing" src="/theme/img/smiley/smiley-laughing.gif" /></p> </div> Creation d'une application Angular22016-09-19T13:14:00+02:002016-09-19T13:14:00+02:00Vincent Mailloltag:autourducode.com,2016-09-19:/angular2-le-tutoriel-interaction-entre-composant.html<p class="first last">Angular2.0 est sortie en version stable, nous allons tester ça.</p> <p>C'est officiel, <strong>Angular 2</strong>, le framework frontend de Google vient de sortir en version stable. voici donc un petit tutoriel dans lequel on va créer une application avec Angular 2 en prenant pour inspiration la série SG1.</p> <p>Première mission installer nodeJS qui va ensuite nous permettre d'installer les modules dont dépend une application Angular2.</p> <p>J'ai choisi la version 4.5 de nodeJS. Si comme moi vous êtes sous Debian voici la ligne de commande que vous pouvez utiliser:</p> <p><kbd class="kbd"> #&nbsp;curl -sL https://deb.nodesource.com/setup_4.x | bash -</kbd> </p> <p><kbd class="kbd"> # apt-get install -y nodejs</kbd> </p> <p>Sinon, je vous laisse consulter <a class="reference external" href="https://nodejs.org/en/download/package-manager/">la doc d'installation de nodeJS</a></p> <p><strong>npm</strong> est en version 2 avec nodeJS 4.5. Il va nous falloir la version 3 pour éviter des messages d'erreur lors de l'installation d'Agular 2.</p> <p><kbd class="kbd"> # npm install -g npm3</kbd> </p> <p>On va créer un répertoire sg1 pour contenir notre projet puis dans ce répertoire, on va générer le fichier package.json.</p> <p><kbd class="kbd"> mkdir sg1</kbd> </p> <p><kbd class="kbd"> cd sg1</kbd> </p> <p><kbd class="kbd"> npm init -y</kbd> </p> <p>On va ensuite installer le compilateur <strong>TypeScript</strong> ainsi que <strong>typings</strong>.</p> <p><kbd class="kbd"> $ npm install --save-dev typescript&#64;2.0.2 typings&#64;1.3.3</kbd> </p> <ul class="simple"> <li>TypeScript est un langage typé et compilé en JS via la commande <strong>tsc</strong>.</li> <li>Typings remplace <strong>tsd</strong>, il permet de connaitre les types utilisés et retournés par les fonctions des librairies en JS qui seront utilisé dans du code TypeScript.</li> </ul> <p>On va lancer le compilateur TypeScript une première fois avec l'option <strong>--init</strong> cela va créer un fichier <strong>tsconfig.json</strong> dans lequel les options de compilation vont être enregistrées. Ce qui nous évitera de toutes les re-tapper à chaque fois que l'on travail sur le projet.</p> <p><kbd class="kbd"> $ tsc --init --t es5 -m commonjs --moduleResolution node --sourceMap --emitDecoratorMetadata --experimentalDecorators --removeComments</kbd> </p> <div class="section" id="installation-d-angular-2"> <h2>Installation d'Angular 2</h2> <p>Angular 2 est décomposé en une dizaine de modules, ce qui permet d'installer seulement ce que l'on utilise.</p> <p><kbd class="kbd"> $ npm install --save &#64;angular/common&#64;2.0.0 &#64;angular/core&#64;2.0.0 &#64;angular/compiler&#64;2.0.0 &#64;angular/platform-browser&#64;2.0.0 &#64;angular/platform-browser-dynamic&#64;2.0.0</kbd> </p> <p>Angular 2 est également dépendant d'autres modules pour fonctionner. Certaine de ces dépendances comme <strong>systemJS</strong> ou <strong>core.js</strong> disparaitront avec l'arrivé d'ES6</p> <p><kbd class="kbd"> $ npm install --save core-js&#64;2.4.1 reflect-metadata&#64;0.1.3 rxjs&#64;5.0.0-beta.12 systemjs&#64;0.19.27 zone.js&#64;0.6.23</kbd> </p> <p>On va installer le fichier de définition de type de core-js.</p> <p><kbd class="kbd"> $ typings install core-js --global --source dt --save</kbd> </p> </div> <div class="section" id="creer-le-point-d-entree-d-une-application-angular-2"> <h2>Créer le point d'entrée d'une application Angular 2</h2> <p>On va créer un répertoire <strong>app</strong> qui contiendra notre application.</p> <p><kbd class="kbd"> $ mkdir app</kbd> </p> <p>Dans ce répertoire, on va créer deux fichiers <strong>app.module.ts</strong> et <strong>main.ts</strong>. Le fichier app.module.ts contiendra la classe <strong>AppModule</strong> qui est le point d'entrée de notre application. Le fichier main.ts contient le code pour démarrer l'AppModule dans un navigateur.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">app/app.module.ts </a></li> <li><a class="reference internal" href="#code-2">app/main.ts </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="kr">import</span> <span class="p">{</span> <span class="nx">NgModule</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">'&#64;angular/core'</span><span class="p">;</span> <span class="kr">import</span> <span class="p">{</span> <span class="nx">BrowserModule</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">'&#64;angular/platform-browser'</span><span class="p">;</span> <span class="kd">&#64;NgModule</span><span class="p">({</span> <span class="nx">imports</span><span class="o">:</span> <span class="p">[</span> <span class="nx">BrowserModule</span> <span class="p">]</span> <span class="p">})</span> <span class="kr">export</span> <span class="kr">class</span> <span class="nx">AppModule</span> <span class="p">{</span> <span class="p">}</span> </pre> </div> </div> <div class="tab-pane" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="kr">import</span> <span class="p">{</span> <span class="nx">platformBrowserDynamic</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">'&#64;angular/platform-browser-dynamic'</span><span class="p">;</span> <span class="kr">import</span> <span class="p">{</span> <span class="nx">AppModule</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">'./app.module'</span><span class="p">;</span> <span class="kr">const</span> <span class="nx">platform</span> <span class="o">=</span> <span class="nx">platformBrowserDynamic</span><span class="p">();</span> <span class="nx">platform</span><span class="p">.</span><span class="nx">bootstrapModule</span><span class="p">(</span><span class="nx">AppModule</span><span class="p">);</span> </pre> </div> </div> </div> </div> <div class="section" id="configurer-systemjs"> <h2>Configurer SystemJS</h2> <p>SystemJS permet d'utiliser est de charger des modules JavaScript normalisé ES6 dans du code ES5. Pour utiliser SystemJS il faut l'importer dans notre index.html ainsi que son fichier de configuration. Le fichier de configuration sera nommé systemjs.config.js et sera à la racine de notre projet. Il indique à SystemJS quel fichier doit être chargé à chaque instruction require est trouvée dans un fichier JS. Ainsi pour l'instruction require(<a class="reference external" href="mailto:'&#64;angular/core">'&#64;angular/core</a>') SystemJS utilisera <a class="reference external" href="mailto:node_modules/&#64;angular/core/bundles/core.umd.js">node_modules/&#64;angular/core/bundles/core.umd.js</a></p> <p>On va également créer notre index.html qui importera angular et SystemJS.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-3">systemjs.config.js</a></li> <li><a class="reference internal" href="#code-4">index.html</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">global</span><span class="p">)</span> <span class="p">{</span> <span class="nx">System</span><span class="p">.</span><span class="nx">config</span><span class="p">({</span> <span class="cm">/* Cette map permet d'indiquer quel fichier il faut charger * en fonction d'un nom du module importé. */</span> <span class="nx">map</span><span class="o">:</span> <span class="p">{</span> <span class="s1">'app'</span><span class="o">:</span> <span class="s1">'app'</span><span class="p">,</span> <span class="s1">'&#64;angular/core'</span><span class="o">:</span> <span class="s1">'node_modules/&#64;angular/core/bundles/core.umd.js'</span><span class="p">,</span> <span class="s1">'&#64;angular/common'</span><span class="o">:</span> <span class="s1">'node_modules/&#64;angular/common/bundles/common.umd.js'</span><span class="p">,</span> <span class="s1">'&#64;angular/compiler'</span><span class="o">:</span> <span class="s1">'node_modules/&#64;angular/compiler/bundles/compiler.umd.js'</span><span class="p">,</span> <span class="s1">'&#64;angular/platform-browser'</span><span class="o">:</span> <span class="s1">'node_modules/&#64;angular/platform-browser/bundles/platform-browser.umd.js'</span><span class="p">,</span> <span class="s1">'&#64;angular/platform-browser-dynamic'</span><span class="o">:</span> <span class="s1">'node_modules/&#64;angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js'</span><span class="p">,</span> <span class="s1">'rxjs'</span><span class="o">:</span> <span class="s1">'node_modules/rxjs'</span> <span class="p">},</span> <span class="cm">/* Le point d'entrée du module app est main.js et * l'extension des fichiers est .js. */</span> <span class="nx">packages</span><span class="o">:</span> <span class="p">{</span> <span class="nx">app</span><span class="o">:</span> <span class="p">{</span> <span class="nx">main</span><span class="o">:</span> <span class="s1">'./main.js'</span><span class="p">,</span> <span class="nx">defaultExtension</span><span class="o">:</span> <span class="s1">'js'</span> <span class="p">},</span> <span class="nx">rxjs</span><span class="o">:</span> <span class="p">{</span> <span class="nx">defaultExtension</span><span class="o">:</span> <span class="s1">'js'</span> <span class="p">}</span> <span class="p">}</span> <span class="p">});</span> <span class="p">})(</span><span class="k">this</span><span class="p">);</span> </pre> </div> </div> <div class="tab-pane" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="p">&lt;</span><span class="nt">html</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>SG 1<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&quot;UTF-8&quot;</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;viewport&quot;</span> <span class="na">content</span><span class="o">=</span><span class="s">&quot;width=device-width, initial-scale=1&quot;</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&quot;node_modules/core-js/client/shim.min.js&quot;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&quot;node_modules/zone.js/dist/zone.js&quot;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&quot;node_modules/reflect-metadata/Reflect.js&quot;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&quot;node_modules/systemjs/dist/system.src.js&quot;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&quot;systemjs.config.js&quot;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> <span class="nx">System</span><span class="p">.</span><span class="kr">import</span><span class="p">(</span><span class="s1">'app'</span><span class="p">).</span><span class="k">catch</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">){</span> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span> <span class="p">});</span> <span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span> </pre> </div> </div> </div> </div> <div class="section" id="creer-notre-premier-composant-angular-2"> <h2>Créer notre premier composant Angular 2</h2> <p><strong>Les composants</strong> sont les briques de base d'Angular 2, C'est une classe qui encapsule un peu de logiques métiers. Cette classe sera décorée avec le décorateur <strong>component</strong> qui définira un template pour afficher la classe.</p> <p>Dans un fichier <strong>app.sgc.ts</strong> on va créer le composant <strong>StargateCommand</strong> qui définie un tableau <strong>members</strong> avec les personnages de la serie de SG1. Le decorateur <strong>Component</strong> permet de définir <strong>le selector</strong>. C'est le nom de la balise que l'on va utiliser pour créer notre composant. Dans le template, la directive <strong>ngFor</strong> va nous permettre de boucler sur les élément contenue dans members et la dirrective [style.color] permet d'aller modifier dirrectement un attribut html. Ici on demande à ce que la couleur de la balise span soit verte si member.health est supérieure à 5 sinon rouge.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-5">app/app.sgc.ts </a></li> <li><a class="reference internal" href="#code-6">index.html</a></li> <li><a class="reference internal" href="#code-7">app.module.ts</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-5"> <div class="highlight"> <pre class="literal-block"> <span class="kr">import</span> <span class="p">{</span> <span class="nx">Component</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">'&#64;angular/core'</span><span class="p">;</span> <span class="kd">&#64;Component</span><span class="p">({</span> <span class="nx">selector</span><span class="o">:</span> <span class="s1">'sgc'</span><span class="p">,</span> <span class="nx">template</span><span class="o">:</span> <span class="sb">` &lt;ul&gt; &lt;li *ngFor=&quot;let member of members&quot;&gt; &lt;span [style.color]=&quot;member.health &gt; 5 ? 'green' : 'red'&quot; &gt;{{ member.name }}&lt;/span&gt; &lt;/li&gt; &lt;/ul&gt; `</span> <span class="p">})</span> <span class="kr">export</span> <span class="kr">class</span> <span class="nx">StargateCommand</span> <span class="p">{</span> <span class="nx">members</span>: <span class="kt">Array</span><span class="o">&lt;</span><span class="nx">any</span><span class="o">&gt;</span> <span class="o">=</span> <span class="p">[</span> <span class="p">{</span><span class="nx">name</span><span class="o">:</span> <span class="s2">&quot;Jack O'neal&quot;</span><span class="p">,</span> <span class="nx">health</span>: <span class="kt">10</span><span class="p">},</span> <span class="p">{</span><span class="nx">name</span><span class="o">:</span> <span class="s2">&quot;Daniel Jackson&quot;</span><span class="p">,</span> <span class="nx">health</span>: <span class="kt">5</span><span class="p">},</span> <span class="p">{</span><span class="nx">name</span><span class="o">:</span> <span class="s2">&quot;Samantha Carter&quot;</span><span class="p">,</span> <span class="nx">health</span>: <span class="kt">10</span><span class="p">},</span> <span class="p">{</span><span class="nx">name</span><span class="o">:</span> <span class="s2">&quot;Teal'c&quot;</span><span class="p">,</span> <span class="nx">health</span>: <span class="kt">4</span><span class="p">},</span> <span class="p">{</span><span class="nx">name</span><span class="o">:</span> <span class="s2">&quot;Vala Mal Doran&quot;</span><span class="p">,</span> <span class="nx">health</span>: <span class="kt">9</span><span class="p">},</span> <span class="p">{</span><span class="nx">name</span><span class="o">:</span> <span class="s2">&quot;Janet Fraiser&quot;</span><span class="p">,</span> <span class="nx">health</span>: <span class="kt">9</span><span class="p">},</span> <span class="p">{</span><span class="nx">name</span><span class="o">:</span> <span class="s2">&quot;Cameron Mitchell&quot;</span><span class="p">,</span> <span class="nx">health</span>: <span class="kt">10</span><span class="p">}</span> <span class="p">];</span> <span class="p">}</span> </pre> </div> </div> <div class="tab-pane" id="code-6"> <div class="highlight"> <pre class="literal-block"> <span class="p">&lt;</span><span class="nt">html</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>Angular 2 QuickStart<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&quot;UTF-8&quot;</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;viewport&quot;</span> <span class="na">content</span><span class="o">=</span><span class="s">&quot;width=device-width, initial-scale=1&quot;</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&quot;node_modules/core-js/client/shim.min.js&quot;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&quot;node_modules/zone.js/dist/zone.js&quot;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&quot;node_modules/reflect-metadata/Reflect.js&quot;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&quot;node_modules/systemjs/dist/system.src.js&quot;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&quot;systemjs.config.js&quot;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> <span class="nx">System</span><span class="p">.</span><span class="kr">import</span><span class="p">(</span><span class="s1">'app'</span><span class="p">).</span><span class="k">catch</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">){</span> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span> <span class="p">});</span> <span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">sgc</span><span class="p">&gt;</span>Loding ...<span class="p">&lt;/</span><span class="nt">sgc</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span> </pre> </div> </div> <div class="tab-pane" id="code-7"> <div class="highlight"> <pre class="literal-block"> import <span class="ss">{</span> NgModule <span class="ss">}</span> from <span class="err">'&#64;</span><span class="s">angular/core'; </span>import <span class="ss">{</span> BrowserModule <span class="ss">}</span> from <span class="err">'&#64;</span><span class="s">angular/platform-browser'; </span>import <span class="ss">{</span> StargateCommand <span class="ss">}</span> from <span class="err">'</span><span class="p">.</span>/app<span class="p">.</span>sgc<span class="err">'</span>; <span class="err">&#64;</span>NgModule<span class="ss">({</span> imports<span class="o">:</span> <span class="ss">[</span> BrowserModule <span class="ss">]</span><span class="p">,</span> declarations<span class="o">:</span> <span class="ss">[</span> StargateCommand <span class="ss">]</span><span class="p">,</span> bootstrap<span class="o">:</span> <span class="ss">[</span> StargateCommand <span class="ss">]</span> <span class="ss">})</span> export class AppModule <span class="ss">{</span> <span class="ss">}</span> </pre> </div> </div> </div> <p>Une fois que l'on a créé notre composant, on va l'importer dans la classe AppModule. Puis l'utiliser dans l'index.html Il nous reste plus qu'à compiler notre application.</p> <p><kbd class="kbd"> tsc -w</kbd> </p> <p>L'option <strong>-w</strong> sert à relancer la compilation à chaque fois qu'un fichier a été modifié puis à lancer un serveur.</p> <p><kbd class="kbd"> python3 -m http.server</kbd> </p> <p>Vous pouvez maintenant visualiser votre application à l'adresse <a class="reference external" href="http://127.0.0.1:8000/">http://127.0.0.1:8000/</a></p> </div> <div class="section" id="passer-des-donnees-du-composant-parent-vers-un-composant-enfant"> <h2>Passer des données du composant parent vers un composant enfant</h2> <p>On va créer un fichier <strong>app.teammember.ts</strong> dans lequel on va définir un composant TeamMember. Puis on va déplacer la logique d'un membre d'équipe dans ce composant. Le composant parent va passer le nom en entrée du composant fils via des attributs. Pour dire qu'une propriété d'un composant est définissable par des attributs en entrée, on utilise <strong>le décorateur &#64;Input</strong></p> <p>On va ajouter notre composant TeamMember dans le tableau <strong>declarations</strong> d'AppModule pour que l'on puisse l'utiliser au sein de l'application. Dans StargateCommand on va importer TeamMember et l'utiliser dans le template.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-8">app/app.teammember.ts</a></li> <li><a class="reference internal" href="#code-9">app/app.sgc.ts</a></li> <li><a class="reference internal" href="#code-10">app/app.module.ts</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-8"> <div class="highlight"> <pre class="literal-block"> import <span class="ss">{</span> Component<span class="p">,</span> Input <span class="ss">}</span> from <span class="err">'&#64;</span><span class="s">angular/core'; </span> <span class="err">&#64;</span>Component<span class="ss">({</span> selector<span class="o">:</span> <span class="err">'</span>team-member<span class="err">'</span><span class="p">,</span> template<span class="o">:</span> <span class="err">`</span> <span class="s">&lt;span</span> <span class="err">[</span><span class="s">style.color</span><span class="err">]</span><span class="s">=&quot;health</span> <span class="s">&gt;</span> <span class="mi">5</span> <span class="err">?</span> <span class="err">'</span>green<span class="err">'</span> <span class="o">:</span> <span class="err">'</span>red<span class="err">'</span>&quot; <span class="o">&gt;</span><span class="ss">{{</span> name <span class="ss">}}</span><span class="s">&lt;/span&gt;</span> <span class="err">`</span> <span class="ss">})</span> export class TeamMember <span class="ss">{</span> <span class="err">&#64;</span>Input<span class="ss">()</span> name<span class="o">:</span> string; <span class="err">&#64;</span>Input<span class="ss">()</span> health<span class="o">:</span> number; <span class="ss">}</span> </pre> </div> </div> <div class="tab-pane" id="code-9"> <div class="highlight"> <pre class="literal-block"> import <span class="ss">{</span> Component <span class="ss">}</span> from <span class="err">'&#64;</span><span class="s">angular/core'; </span>import <span class="ss">{</span> TeamMember <span class="ss">}</span> from <span class="err">'</span><span class="p">.</span>/app<span class="p">.</span>teammember<span class="err">'</span>; <span class="err">&#64;</span>Component<span class="ss">({</span> selector<span class="o">:</span> <span class="err">'</span>sgc<span class="err">'</span><span class="p">,</span> template<span class="o">:</span> <span class="err">`</span> <span class="s">&lt;ul&gt;</span> <span class="s">&lt;li</span> <span class="s">*ngFor=&quot;let</span> <span class="s">member</span> <span class="s">of</span> <span class="s">members&quot;&gt;</span> <span class="o">&lt;</span>team-member <span class="ss">[</span>name<span class="ss">]</span><span class="o">=</span>&quot;member<span class="p">.</span>name&quot; <span class="ss">[</span>health<span class="ss">]</span><span class="o">=</span>&quot;member<span class="p">.</span>health&quot;<span class="o">&gt;</span><span class="s">&lt;/team-member&gt;</span> <span class="s">&lt;/li&gt;</span> <span class="s">&lt;/ul&gt;</span> <span class="err">`</span> <span class="ss">})</span> export class StargateCommand <span class="ss">{</span> members<span class="o">:</span> Array<span class="s">&lt;any&gt;</span> <span class="o">=</span> <span class="ss">[</span> <span class="ss">{</span>name<span class="o">:</span> &quot;Jack O<span class="err">'</span>neal&quot;<span class="p">,</span> health<span class="o">:</span> <span class="mi">10</span><span class="ss">}</span><span class="p">,</span> <span class="ss">{</span>name<span class="o">:</span> &quot;Daniel Jackson&quot;<span class="p">,</span> health<span class="o">:</span> <span class="mi">5</span><span class="ss">}</span><span class="p">,</span> <span class="ss">{</span>name<span class="o">:</span> &quot;Samantha Carter&quot;<span class="p">,</span> health<span class="o">:</span> <span class="mi">10</span><span class="ss">}</span><span class="p">,</span> <span class="ss">{</span>name<span class="o">:</span> &quot;Teal<span class="err">'</span>c&quot;<span class="p">,</span> health<span class="o">:</span> <span class="mi">4</span><span class="ss">}</span><span class="p">,</span> <span class="ss">{</span>name<span class="o">:</span> &quot;Vala Mal Doran&quot;<span class="p">,</span> health<span class="o">:</span> <span class="mi">9</span><span class="ss">}</span><span class="p">,</span> <span class="ss">{</span>name<span class="o">:</span> &quot;Janet Fraiser&quot;<span class="p">,</span> health<span class="o">:</span> <span class="mi">9</span><span class="ss">}</span><span class="p">,</span> <span class="ss">{</span>name<span class="o">:</span> &quot;Cameron Mitchell&quot;<span class="p">,</span> health<span class="o">:</span> <span class="mi">10</span><span class="ss">}</span> <span class="ss">]</span>; <span class="ss">}</span> </pre> </div> </div> <div class="tab-pane" id="code-10"> <div class="highlight"> <pre class="literal-block"> import <span class="ss">{</span> NgModule <span class="ss">}</span> from <span class="err">'&#64;</span><span class="s">angular/core'; </span>import <span class="ss">{</span> BrowserModule <span class="ss">}</span> from <span class="err">'&#64;</span><span class="s">angular/platform-browser'; </span>import <span class="ss">{</span> StargateCommand <span class="ss">}</span> from <span class="err">'</span><span class="p">.</span>/app<span class="p">.</span>sgc<span class="err">'</span>; import <span class="ss">{</span> TeamMember <span class="ss">}</span> from <span class="err">'</span><span class="p">.</span>/app<span class="p">.</span>teammember<span class="err">'</span>; <span class="err">&#64;</span>NgModule<span class="ss">({</span> imports<span class="o">:</span> <span class="ss">[</span> BrowserModule <span class="ss">]</span><span class="p">,</span> declarations<span class="o">:</span> <span class="ss">[</span> StargateCommand<span class="p">,</span> TeamMember <span class="ss">]</span><span class="p">,</span> bootstrap<span class="o">:</span> <span class="ss">[</span> StargateCommand <span class="ss">]</span> <span class="ss">})</span> export class AppModule <span class="ss">{</span> <span class="ss">}</span> </pre> </div> </div> </div> </div> <div class="section" id="ecouter-un-evenement"> <h2>Écouter un événement</h2> <p>Pour écouter un événement on met le nom de celui-ci entre deux parenthèses et on met comme valeur le nom d'une fonction à appeler.</p> <p>Dans notre exemple, chaque membre de l'équipe aura un bouton pour l'envoyer en mission il reviendra de mission au bout de 2 seconde. Pour réaliser cela on va ajouter deux méthodes a la classe TeamMember.</p> <ul class="simple"> <li>exploreTheGalaxy qui va désactivé le bouton.</li> <li>endOfMission qui est appelé deux secondes après l'appel d'exploreTheGalaxy et qui réactive le bouton.</li> </ul> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-11">app/app.teammember.ts</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-11"> <div class="highlight"> <pre class="literal-block"> import <span class="ss">{</span> Component<span class="p">,</span> Input <span class="ss">}</span> from <span class="err">'&#64;</span><span class="s">angular/core'; </span> <span class="err">&#64;</span>Component<span class="ss">({</span> selector<span class="o">:</span> <span class="err">'</span>team-member<span class="err">'</span><span class="p">,</span> template<span class="o">:</span> <span class="err">`</span> <span class="s">&lt;span</span> <span class="err">[</span><span class="s">style.color</span><span class="err">]</span><span class="s">=&quot;health</span> <span class="s">&gt;</span> <span class="mi">5</span> <span class="err">?</span> <span class="err">'</span>green<span class="err">'</span> <span class="o">:</span> <span class="err">'</span>red<span class="err">'</span>&quot; <span class="o">&gt;</span><span class="ss">{{</span> name <span class="ss">}}</span><span class="s">&lt;/span&gt;</span> <span class="s">&lt;button</span> <span class="s">(click)=&quot;exploreTheGalaxy()&quot;</span> <span class="err">[</span><span class="s">disabled</span><span class="err">]</span><span class="s">=&quot;!ready&quot;&gt;</span><span class="ss">{{</span> labelButton <span class="ss">}}</span><span class="s">&lt;/button&gt;</span> <span class="err">`</span> <span class="ss">})</span> export class TeamMember <span class="ss">{</span> <span class="err">&#64;</span>Input<span class="ss">()</span> name<span class="o">:</span> string; <span class="err">&#64;</span>Input<span class="ss">()</span> health<span class="o">:</span> number; ready<span class="o">:</span> boolean <span class="o">=</span> true; labelButton<span class="o">:</span> string <span class="o">=</span> <span class="err">'</span>Explore the galaxy<span class="err">'</span>; endOfMission <span class="o">=</span> function<span class="ss">()</span> <span class="ss">{</span> this<span class="p">.</span>ready <span class="o">=</span> true; this<span class="p">.</span>labelButton <span class="o">=</span> <span class="err">'</span>Explore the galaxy<span class="err">'</span>; <span class="ss">}</span>; exploreTheGalaxy<span class="ss">()</span> <span class="ss">{</span> <span class="nf">if</span> <span class="ss">(</span>this<span class="p">.</span>ready<span class="ss">)</span> <span class="ss">{</span> this<span class="p">.</span>ready <span class="o">=</span> false; this<span class="p">.</span>labelButton <span class="o">=</span> <span class="err">'</span>Despatched<span class="err">'</span>; setTimeout<span class="ss">(()</span> <span class="o">=&gt;</span> this<span class="p">.</span>endOfMission<span class="ss">()</span><span class="p">,</span> <span class="mi">2000</span><span class="ss">)</span>; <span class="ss">}</span> <span class="ss">}</span> <span class="ss">}</span> </pre> </div> </div> </div> </div> <div class="section" id="creer-c-est-propre-evenement"> <h2>Créer c'est propre événement</h2> <p>Un composant peut recevoir des données en entrée par un composant parent, mais il peut également notifier un composant parent d'un changement d'état. Pour ce faire on va utiliser le décorateur <strong>&#64;Output</strong> pour indiquer quel événement un composant parent peut écouter. Les événements doivent être des objets de type <strong>EventEmitter</strong></p> <p>Dans notre exemple un composant TeamMember enverra un événement MissionDone lorsqu'il rentrera de mission.</p> <p>Le composant StargateCommand aura une méthode onMissionDone qu'il va lier à l'événement. la méthode onMissionDone affichera le nom de la personne qui vient de rentrer.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-12">app/app.teammember.ts</a></li> <li><a class="reference internal" href="#code-13">app/app.sgc.ts</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-12"> <div class="highlight"> <pre class="literal-block"> import <span class="ss">{</span> Component<span class="p">,</span> EventEmitter<span class="p">,</span> Input<span class="p">,</span> Output <span class="ss">}</span> from <span class="err">'&#64;</span><span class="s">angular/core'; </span> <span class="err">&#64;</span>Component<span class="ss">({</span> selector<span class="o">:</span> <span class="err">'</span>team-member<span class="err">'</span><span class="p">,</span> template<span class="o">:</span> <span class="err">`</span> <span class="s">&lt;span</span> <span class="err">[</span><span class="s">style.color</span><span class="err">]</span><span class="s">=&quot;health</span> <span class="s">&gt;</span> <span class="mi">5</span> <span class="err">?</span> <span class="err">'</span>green<span class="err">'</span> <span class="o">:</span> <span class="err">'</span>red<span class="err">'</span>&quot; <span class="o">&gt;</span><span class="ss">{{</span> name <span class="ss">}}</span><span class="s">&lt;/span&gt;</span> <span class="s">&lt;button</span> <span class="s">(click)=&quot;exploreTheGalaxy()&quot;</span> <span class="err">[</span><span class="s">disabled</span><span class="err">]</span><span class="s">=&quot;!ready&quot;&gt;</span><span class="ss">{{</span> labelButton <span class="ss">}}</span><span class="s">&lt;/button&gt;</span> <span class="err">`</span> <span class="ss">})</span> export class TeamMember <span class="ss">{</span> <span class="err">&#64;</span>Input<span class="ss">()</span> name<span class="o">:</span> string; <span class="err">&#64;</span>Input<span class="ss">()</span> health<span class="o">:</span> number; <span class="err">&#64;</span>Output<span class="ss">()</span> missionDone <span class="o">=</span> new EventEmitter<span class="s">&lt;TeamMember&gt;</span><span class="ss">()</span>; ready<span class="o">:</span> boolean <span class="o">=</span> true; labelButton<span class="o">:</span> string <span class="o">=</span> <span class="err">'</span>Explore the galaxy<span class="err">'</span>; endOfMission <span class="o">=</span> function<span class="ss">()</span> <span class="ss">{</span> this<span class="p">.</span>ready <span class="o">=</span> true; this<span class="p">.</span>labelButton <span class="o">=</span> <span class="err">'</span>Explore the galaxy<span class="err">'</span>; this<span class="p">.</span>missionDone<span class="p">.</span>emit<span class="ss">(</span>this<span class="ss">)</span>; <span class="ss">}</span>; exploreTheGalaxy<span class="ss">()</span> <span class="ss">{</span> <span class="nf">if</span> <span class="ss">(</span>this<span class="p">.</span>ready<span class="ss">)</span> <span class="ss">{</span> this<span class="p">.</span>ready <span class="o">=</span> false; this<span class="p">.</span>labelButton <span class="o">=</span> <span class="err">'</span>Despatched<span class="err">'</span>; setTimeout<span class="ss">(()</span> <span class="o">=&gt;</span> this<span class="p">.</span>endOfMission<span class="ss">()</span><span class="p">,</span> <span class="mi">2000</span><span class="ss">)</span>; <span class="ss">}</span> <span class="ss">}</span> <span class="ss">}</span> </pre> </div> </div> <div class="tab-pane" id="code-13"> <div class="highlight"> <pre class="literal-block"> import <span class="ss">{</span> Component <span class="ss">}</span> from <span class="err">'&#64;</span><span class="s">angular/core'; </span>import <span class="ss">{</span> TeamMember <span class="ss">}</span> from <span class="err">'</span><span class="p">.</span>/app<span class="p">.</span>teammember<span class="err">'</span>; <span class="err">&#64;</span>Component<span class="ss">({</span> selector<span class="o">:</span> <span class="err">'</span>sgc<span class="err">'</span><span class="p">,</span> template<span class="o">:</span> <span class="err">`</span> <span class="s">&lt;p&gt;</span><span class="ss">{{</span> message <span class="ss">}}</span><span class="s">&lt;/p&gt;</span> <span class="s">&lt;ul&gt;</span> <span class="s">&lt;li</span> <span class="s">*ngFor=&quot;let</span> <span class="s">member</span> <span class="s">of</span> <span class="s">members&quot;&gt;</span> <span class="o">&lt;</span>team-member <span class="ss">[</span>name<span class="ss">]</span><span class="o">=</span>&quot;member<span class="p">.</span>name&quot; <span class="ss">[</span>health<span class="ss">]</span><span class="o">=</span>&quot;member<span class="p">.</span>health&quot; <span class="ss">(</span>missionDone<span class="ss">)</span><span class="o">=</span>&quot;onMissionDone<span class="ss">(</span><span class="err">$</span>event<span class="ss">)</span>&quot;<span class="o">&gt;</span><span class="s">&lt;/team-member&gt;</span> <span class="s">&lt;/li&gt;</span> <span class="s">&lt;/ul&gt;</span> <span class="err">`</span> <span class="ss">})</span> export class StargateCommand <span class="ss">{</span> message<span class="o">:</span>string; onMissionDone<span class="ss">(</span>teamMember<span class="ss">)</span> <span class="ss">{</span> this<span class="p">.</span>message <span class="o">=</span> teamMember<span class="p">.</span>name <span class="o">+</span> &quot; had just returned from a mission&quot;; <span class="ss">}</span> members<span class="o">:</span> Array<span class="s">&lt;any&gt;</span> <span class="o">=</span> <span class="ss">[</span> <span class="ss">{</span>name<span class="o">:</span> &quot;Jack O<span class="err">'</span>neal&quot;<span class="p">,</span> health<span class="o">:</span> <span class="mi">10</span><span class="ss">}</span><span class="p">,</span> <span class="ss">{</span>name<span class="o">:</span> &quot;Daniel Jackson&quot;<span class="p">,</span> health<span class="o">:</span> <span class="mi">5</span><span class="ss">}</span><span class="p">,</span> <span class="ss">{</span>name<span class="o">:</span> &quot;Samantha Carter&quot;<span class="p">,</span> health<span class="o">:</span> <span class="mi">10</span><span class="ss">}</span><span class="p">,</span> <span class="ss">{</span>name<span class="o">:</span> &quot;Teal<span class="err">'</span>c&quot;<span class="p">,</span> health<span class="o">:</span> <span class="mi">4</span><span class="ss">}</span><span class="p">,</span> <span class="ss">{</span>name<span class="o">:</span> &quot;Vala Mal Doran&quot;<span class="p">,</span> health<span class="o">:</span> <span class="mi">9</span><span class="ss">}</span><span class="p">,</span> <span class="ss">{</span>name<span class="o">:</span> &quot;Janet Fraiser&quot;<span class="p">,</span> health<span class="o">:</span> <span class="mi">9</span><span class="ss">}</span><span class="p">,</span> <span class="ss">{</span>name<span class="o">:</span> &quot;Cameron Mitchell&quot;<span class="p">,</span> health<span class="o">:</span> <span class="mi">10</span><span class="ss">}</span> <span class="ss">]</span>; <span class="ss">}</span> </pre> </div> </div> </div> <p>Voilou j'espère que ça vous a plu et que la quantité de librairie à installer ne vous a pas découragé. En attendant un prochain tuto, à vos IDE et bon code <img alt="laughing" src="/theme/img/smiley/smiley-laughing.gif" /></p> </div> Les gestionnaires de tâche: Grunt2016-08-05T16:11:00+02:002016-08-05T16:11:00+02:00Vincent Mailloltag:autourducode.com,2016-08-05:/gestionnaire-de-taches-grunt.html<p class="first last">Aujourd'hui on va parler de gestionnaire de tâche. Dans un projet, il y a souvent une multitude de tâches redondantes comme la compilation, le lancement des tests ou la création de fichier de configuration dédié à l'environnement de développement. Un gestionnaire de tâches permet d'avoir un seul point d'entrée pour exécuter toutes ces commandes et de gérer les dépendances entre elles.</p> <p>Aujourd'hui on va parler de gestionnaire de tâche. Dans un projet, il y a souvent une multitude de tâches redondantes comme la compilation, le lancement des tests ou la création de fichier de configuration dédié à l'environnement de développement. Un gestionnaire de tâches permet d'avoir un seul point d'entrée pour exécuter toutes ces commandes et de gérer les dépendances entre elles.</p> <p><strong>Grunt</strong> est un outil qui s'exécute dans un environnement <strong>NodeJS</strong> donc la première chose et d'avoir cet environnement installé. Ensuite, il y a deux parties à installer le <strong>CLI</strong> (Command Line Interface) qui est globale à la machine et le <strong>gestionnaire de tâche</strong> lui-même qui est dépendant du projet. Cela permet d'avoir plusieurs projets avec des versions de <strong>Grunt</strong> différentes sur la même machine. L'installation du CLI se fait avec la commande suivante.</p> <p><kbd class="kbd"> # npm install -g grunt-cli</kbd> </p> <p>Pour installer le gestionnaire de taches placez vous dans le répertoire de votre projet. Si votre projet ne contient pas de fichier <strong>package.json</strong>, on va en créer un. Cela va permettre par la suite à une personne qui récupère votre projet d'installer la même version de <strong>Grunt</strong> ainsi que les différents plugins grunt utilisés dans le projet. Le fichier <strong>package.json</strong> se créé via la commande:</p> <p><kbd class="kbd"> $ npm init</kbd> </p> <p>Saisissez ensuite la commande suivante:</p> <p><kbd class="kbd"> $ npm install grunt --save-dev</kbd> </p> <p>Le <strong>--save-dev</strong> permet de déclarer l'installation de <strong>Grunt</strong> dans le package.json. Maintenant que tout est en place, c'est le moment de déclarer nos tâches dans un fichier <strong>Gruntfile.js</strong> Ce fichier doit déclarer une fonction qui prend une instance de Grunt en paramètre.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">Gruntfile.js</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">grunt</span><span class="p">)</span> <span class="p">{</span> <span class="p">};</span> </pre> </div> </div> </div> <p>Lorsque l'on souhaite ajouter une tâche on utilise la fonction <strong>grunt.registerTask</strong> le premier paramètre est le nom de la tâche le deuxième est facultatif, c'est la description de la tâche et le dernier est la fonction à exécuter.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-2">Gruntfile.js</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">grunt</span><span class="p">)</span> <span class="p">{</span> <span class="nx">grunt</span><span class="p">.</span><span class="nx">registerTask</span><span class="p">(</span><span class="s1">'hello'</span><span class="p">,</span> <span class="s1">'Print hello.'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="nx">grunt</span><span class="p">.</span><span class="nx">log</span><span class="p">.</span><span class="nx">writeln</span><span class="p">(</span><span class="s1">'Hello world'</span><span class="p">);</span> <span class="p">});</span> <span class="p">};</span> </pre> </div> </div> </div> <p>Si l'on veut exécuter la commande hello on tapera grunt hello dans un terminal.</p> <p><kbd class="kbd"> $ grunt hello</kbd> </p> <p>Une des fonctions de Grunt utiles pour écrire vos propre tâches est <strong>grunt.util.spawn</strong> qui permet de faire des appels systèmes mais, vous risquez de ne plus être cross plate-form.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-3">Gruntfile.js</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="nx">grunt</span><span class="p">.</span><span class="nx">registerTask</span><span class="p">(</span><span class="s1">'ls'</span><span class="p">,</span> <span class="s1">'Display file listing'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">done</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">async</span><span class="p">();</span> <span class="nx">grunt</span><span class="p">.</span><span class="nx">util</span><span class="p">.</span><span class="nx">spawn</span><span class="p">({</span> <span class="nx">cmd</span><span class="o">:</span> <span class="s1">'ls'</span><span class="p">,</span> <span class="nx">args</span><span class="o">:</span> <span class="p">[</span><span class="s1">'-l'</span><span class="p">]</span> <span class="p">},</span> <span class="kd">function</span> <span class="nx">doneFunction</span><span class="p">(</span><span class="nx">error</span><span class="p">,</span> <span class="nx">result</span><span class="p">,</span> <span class="nx">code</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> <span class="nx">grunt</span><span class="p">.</span><span class="nx">fail</span><span class="p">.</span><span class="nx">warn</span><span class="p">(</span><span class="nx">error</span><span class="p">,</span> <span class="nx">code</span><span class="p">);</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="nx">grunt</span><span class="p">.</span><span class="nx">log</span><span class="p">.</span><span class="nx">ok</span><span class="p">(</span><span class="nb">String</span><span class="p">(</span><span class="nx">result</span><span class="p">));</span> <span class="p">}</span> <span class="nx">done</span><span class="p">();</span> <span class="p">}</span> <span class="p">);</span> <span class="p">});</span> </pre> </div> </div> </div> <p>Il est d'usage de créer une tâche 'default' dans notre <strong>Gruntfile</strong>. La tâche default est accessible simplement en tapant grunt.</p> <p>Dans notre exemple, elle va appeler la tâche 'hello' puis 'ls'</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-4">Gruntfile.js</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="nx">grunt</span><span class="p">.</span><span class="nx">registerTask</span><span class="p">(</span><span class="s1">'default'</span><span class="p">,</span> <span class="p">[</span><span class="s1">'hello'</span><span class="p">,</span> <span class="s1">'ls'</span><span class="p">])</span> </pre> </div> </div> </div> <div class="section" id="les-plugins-grunt-ne-developpez-plus-vos-taches-configurez-les"> <h2>Les plugins grunt, ne développez plus vos tâches, configurez-les !</h2> <p>Maintenant que nous avons vu le fonctionnement de Grunt, Nous allons voir ce qui fait ça force, le système de plugin. Dans la pratique vous aurez rarement à développer vos tâches comme on la fait ci-dessous, mais plus à installer des plugins et à les configurer. Pour exemple, on va prendre le plugin <strong>grunt copy</strong> qui permet de copier des fichier et répertoire. Dans Grunt, tous les plugins se configurent de la même façon pour ce qui est des entrées/sortie donc si vous savez configurer le plugin copy vous saurez configurer tous les plugins.</p> <p>Commençons par installer le plugin copy:</p> <p><kbd class="kbd"> $ npm install --only=dev grunt-contrib-copy</kbd> </p> <p>Maintenant il faut indiquer dans notre Gruntfile que l'on souhaite utiliser grunt copy:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-5">Gruntfile.js</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-5"> <div class="highlight"> <pre class="literal-block"> <span class="nx">grunt</span><span class="p">.</span><span class="nx">loadNpmTasks</span><span class="p">(</span><span class="s1">'grunt-contrib-copy'</span><span class="p">);</span> </pre> </div> </div> </div> <p>Puis on va le configurer avec la fonction grunt.initConfig qui prend en paramètre un objet contenant les configurations des plugins que l'on a ajoutés.</p> <p>Chaque plugin peut avoir plusieurs configurations au sein d'un même Gruntfile c'est ce que l'on appelle des cibles. Par exemple vous pouvez avoir besoin du plugin copy lors de la distribution de votre projet mais également lors de la construction de la documentation. On va donc déclarer les cibles dist et doc de copy.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-6">Gruntfile.js</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-6"> <div class="highlight"> <pre class="literal-block"> <span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">grunt</span><span class="p">)</span> <span class="p">{</span> <span class="nx">grunt</span><span class="p">.</span><span class="nx">loadNpmTasks</span><span class="p">(</span><span class="s1">'grunt-contrib-copy'</span><span class="p">);</span> <span class="nx">grunt</span><span class="p">.</span><span class="nx">initConfig</span><span class="p">({</span> <span class="nx">copy</span><span class="o">:</span> <span class="p">{</span> <span class="nx">dist</span><span class="o">:</span> <span class="p">{</span> <span class="p">},</span> <span class="nx">doc</span><span class="o">:</span> <span class="p">{</span> <span class="p">}</span> <span class="p">}</span> <span class="p">});</span> <span class="p">}</span> </pre> </div> </div> </div> <p>Au sein de ces cibles, nous allons indiquer les entrées/sorties en passant un tableau d'objet à la propriété files.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-7">Gruntfile.js</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-7"> <div class="highlight"> <pre class="literal-block"> <span class="nx">grunt</span><span class="p">.</span><span class="nx">initConfig</span><span class="p">({</span> <span class="nx">copy</span><span class="o">:</span> <span class="p">{</span> <span class="nx">dist</span><span class="o">:</span> <span class="p">{</span> <span class="nx">files</span><span class="o">:</span> <span class="p">[</span> <span class="p">{</span><span class="nx">src</span><span class="o">:</span> <span class="p">[</span><span class="s1">'src/a.css'</span><span class="p">,</span> <span class="s1">'src/b.css'</span><span class="p">],</span> <span class="nx">dest</span><span class="o">:</span> <span class="s1">'dest/'</span><span class="p">},</span> <span class="p">]</span> <span class="p">},</span> <span class="nx">doc</span><span class="o">:</span> <span class="p">{</span> <span class="p">}</span> <span class="p">}</span> <span class="p">});</span> </pre> </div> </div> </div> <p>Dans l'exemple ci-dessus lorsque l'on va saisir la commande:</p> <p><kbd class="kbd"> $ grunt copy:dist</kbd> </p> <p>Le plugin va utiliser les chemins src/a.css et src/b.css du répertoire courant en entrée et les destinations dest/src/a.css et dest/src/b.css. Le répertoire courant sera toujours le même que l'emplacement du Gruntfile.</p> <p>Si ce n'est pas ce que vous souhaitez et que vous préférez les destinations dest/a.css et dest/b.css. Il y a plusieurs possibilités.</p> <p>L'une consiste à utiliser le mode <strong>expand</strong> et de données un répertoire à partir duquel les sources seront lues via la propriété <strong>cwd</strong>:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-8">Gruntfile.js</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-8"> <div class="highlight"> <pre class="literal-block"> <span class="nx">files</span><span class="o">:</span> <span class="p">[</span> <span class="p">{</span><span class="nx">expand</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">cwd</span><span class="o">:</span> <span class="s1">'src'</span><span class="p">,</span> <span class="nx">src</span><span class="o">:</span> <span class="p">[</span><span class="s1">'a.css'</span><span class="p">,</span> <span class="s1">'b.css'</span><span class="p">],</span> <span class="nx">dest</span><span class="o">:</span> <span class="s1">'dest/'</span><span class="p">},</span> <span class="p">]</span> </pre> </div> </div> </div> <p>a et b sont cherchés dans le répertoire src et placés dans dest/</p> <p>On peut également utiliser propriété <strong>flatten</strong> pour indiquer d'utiliser que le nom de fichier dans la génération du chemin de sortie.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-9">Gruntfile.js</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-9"> <div class="highlight"> <pre class="literal-block"> <span class="nx">files</span><span class="o">:</span> <span class="p">[</span> <span class="p">{</span><span class="nx">expand</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">flatten</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">src</span><span class="o">:</span> <span class="p">[</span><span class="s1">'src/a.css'</span><span class="p">,</span> <span class="s1">'src/b.css'</span><span class="p">],</span> <span class="nx">dest</span><span class="o">:</span> <span class="s1">'dest/'</span><span class="p">},</span> <span class="p">]</span> </pre> </div> </div> </div> </div> <div class="section" id="changer-l-extension-du-fichier"> <h2>Changer l'extension du fichier.</h2> <p>On peut changer l'extension du fichier de sortie grâce à <strong>'ext'</strong></p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-10">Gruntfile.js</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-10"> <div class="highlight"> <pre class="literal-block"> <span class="nx">files</span><span class="o">:</span> <span class="p">[</span> <span class="p">{</span><span class="nx">expand</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">flatten</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">ext</span><span class="o">:</span> <span class="s1">'.js'</span><span class="p">,</span> <span class="nx">src</span><span class="o">:</span> <span class="p">[</span><span class="s1">'src/ts/a.ts'</span><span class="p">,</span> <span class="s1">'src/ts/b.ts'</span><span class="p">],</span> <span class="nx">dest</span><span class="o">:</span> <span class="s1">'dest/js/'</span><span class="p">},</span> <span class="p">]</span> </pre> </div> </div> </div> </div> <div class="section" id="renommer-une-partie-du-chemin"> <h2>Renommer une partie du chemin</h2> <p>On peut utiliser une fonction passée à la propriété rename pour modifier le chemin. Par exemple si l'on souhaite que les fichiers suffixés par &quot;-en&quot; soit placés dans un sous-répertoire en et que les fichiers suffixés par &quot;-fr&quot; soit placés dans fr on peut écrire ceci, après avoir importé le module path.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-11">Gruntfile.js</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-11"> <div class="highlight"> <pre class="literal-block"> <span class="nx">files</span><span class="o">:</span> <span class="p">[</span> <span class="p">{</span><span class="nx">expand</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">rename</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">dest</span><span class="p">,</span> <span class="nx">src</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">srcObj</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">src</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">langDir</span> <span class="o">=</span> <span class="nx">srcObj</span><span class="p">.</span><span class="nx">name</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s1">'-'</span><span class="p">)[</span><span class="mi">1</span><span class="p">];</span> <span class="kd">var</span> <span class="nx">fileName</span> <span class="o">=</span> <span class="nx">srcObj</span><span class="p">.</span><span class="nx">name</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s1">'-'</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="nx">srcObj</span><span class="p">.</span><span class="nx">ext</span><span class="p">;</span> <span class="k">return</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">dest</span><span class="p">,</span> <span class="nx">langDir</span><span class="p">,</span> <span class="nx">fileName</span><span class="p">);</span> <span class="p">},</span> <span class="nx">src</span><span class="o">:</span> <span class="p">[</span><span class="s1">'doc/a-fr.rst'</span><span class="p">,</span> <span class="s1">'doc/a-en.rst'</span><span class="p">],</span> <span class="nx">dest</span><span class="o">:</span> <span class="s1">'dest/doc/'</span><span class="p">}</span> <span class="p">]</span> </pre> </div> </div> </div> </div> <div class="section" id="la-syntaxe-glob"> <h2>La syntaxe glob</h2> <p>Il est possible d'utiliser la syntaxe glob dans les chemins de fichier ainsi</p> <p><pre> src: ['doc/a-fr.rst', 'doc/a-en.rst']</pre> </p> <p>peut s'écrire:</p> <p><pre> src: ['doc/\*.rst']</pre> </p> <p>Ce qui évite d'avoir à rééditer le Gruntfile à chaque ajout de nouveau fichier.</p> </div> <div class="section" id="les-raccourcis"> <h2>Les raccourcis</h2> <p>Les raccourcis de syntaxe sont utilisables que dans certain cas.</p> <p>Si votre plugin n'utilise n'a pas de sortie c'est par exemple le cas comme des analyseurs statiques (jslint, pylint...) ou qu'une seule sortie, vous pouvez utiliser les propriétés src et dest directement à la place de la propriété files.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-12">Gruntfile.js</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-12"> <div class="highlight"> <pre class="literal-block"> <span class="nx">uneCible</span><span class="o">:</span> <span class="p">{</span> <span class="nx">src</span><span class="o">:</span> <span class="p">[</span><span class="s2">&quot;fichier.1&quot;</span><span class="p">,</span> <span class="s2">&quot;fichier.2&quot;</span><span class="p">]</span> <span class="p">}</span> <span class="c1">// ou </span> <span class="nx">uneCible</span><span class="o">:</span> <span class="p">{</span> <span class="nx">src</span><span class="o">:</span> <span class="p">[</span><span class="s2">&quot;fichier.1&quot;</span><span class="p">,</span> <span class="s2">&quot;fichier.2&quot;</span><span class="p">],</span> <span class="nx">dest</span><span class="o">:</span> <span class="s2">&quot;sortie&quot;</span> <span class="p">}</span> </pre> </div> </div> </div> <p>Si vous n'utilisez pas le mode expand, vous pouvez utiliser un objet pour valeur de la propriété files:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-13">Gruntfile.js</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-13"> <div class="highlight"> <pre class="literal-block"> <span class="nx">files</span><span class="o">:</span> <span class="p">{</span> <span class="s1">'dest/sortie.1'</span><span class="o">:</span> <span class="p">[</span><span class="s1">'src/fichier.11'</span><span class="p">,</span> <span class="s1">'src/fichier.12'</span><span class="p">],</span> <span class="s1">'dest/sortie.2'</span><span class="o">:</span> <span class="p">[</span><span class="s1">'src/fichier.21'</span><span class="p">,</span> <span class="s1">'src/fichier.22'</span><span class="p">],</span> <span class="p">}</span> </pre> </div> </div> </div> <p>Personnellement, je préfère ne pas utiliser les raccourcis, car le Gruntfile est plus fastidieux à relire et à modifier s'il mélange les trois types de syntaxe.</p> <p>Voilà, j'espère que ça vous a plu, à vos IDE et bon code. <img alt="laughing" src="/theme/img/smiley/smiley-laughing.gif" /></p> </div> Des variables et des conditions dans une regex2016-03-20T21:57:00+01:002016-03-20T21:57:00+01:00Vincent Mailloltag:autourducode.com,2016-03-20:/variables-et-conditions-dans-une-regex.html<p class="first last">Vous connaissez certainement les regexs appelées également expressions rationnelles mais saviez-vous qu'on pouvait définir des conditions en regex ?</p> <p>Vous connaissez certainement les regexs appelées également expressions rationnelles. Le principe est de décrire un ensemble de chaine de caractère qui ont certain point en commun. Par exemple, la regex T.T correspond à toutes chaines de caractère qui ont un T suivi par n'importe qu'elle caractère suivi d'un autre T. Dans une regex, le point correspond à tout caractère qui n'est pas un saut de ligne. La plus part des langages de programmation supportent les regexs. En python, on utilses le module re et en JS les regexs sont intégrées au langage et s'écrive entre deux slashs ('/'). Pour voir si un motif est présent dans une chaîne de caractère on utilisera la méthode <strong>exec</strong> en JS et <strong>search</strong> en python.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">Python</a></li> <li><a class="reference internal" href="#code-2">JS</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="n">match</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="s2">&quot;T.T&quot;</span><span class="p">,</span> <span class="s2">&quot;uuuToTuuu&quot;</span><span class="p">):</span> <span class="k">print</span><span class="p">(</span><span class="n">match</span><span class="p">)</span> <span class="c1"># &lt;_sre.SRE_Match object; span=(3, 6), match='ToT'&gt;</span> <span class="n">match</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="s2">&quot;T.T&quot;</span><span class="p">,</span> <span class="s2">&quot;uuuTaTuuu&quot;</span><span class="p">)</span> <span class="k">print</span><span class="p">(</span><span class="n">match</span><span class="p">)</span> <span class="c1"># &lt;_sre.SRE_Match object; span=(3, 6), match='TaT'&gt;</span> <span class="n">match</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="s2">&quot;T.T&quot;</span><span class="p">,</span> <span class="s2">&quot;uuuBTPuuu&quot;</span><span class="p">)</span> <span class="k">print</span><span class="p">(</span><span class="n">match</span><span class="p">)</span> <span class="c1"># None</span> </pre> </div> </div> <div class="tab-pane" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="nx">match</span> <span class="o">=</span> <span class="sr">/T.T/</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span><span class="s2">&quot;uuuToTuuu&quot;</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">match</span><span class="p">);</span> <span class="c1">// [ 'ToT', index: 3, input: 'uuuToTuuu' ] </span><span class="nx">match</span> <span class="o">=</span> <span class="sr">/T.T/</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span><span class="s2">&quot;uuuTaTuuu&quot;</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">match</span><span class="p">);</span> <span class="c1">// [ 'TaT', index: 3, input: 'uuuTaTuuu' ] </span><span class="nx">match</span> <span class="o">=</span> <span class="sr">/T.T/</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span><span class="s2">&quot;uuuBTPuuu&quot;</span><span class="p">)</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">match</span><span class="p">);</span> <span class="c1">// null </span> </pre> </div> </div> </div> <p>Dans une regex à l'instar du point, d'autre caractère ou couple de caractères sont utilisés pour d'écrire un ensemble de caractère possible, on appelle cela une classe.</p> <ul class="simple"> <li><strong>d</strong> pour les chiffres.</li> <li><strong>w</strong> pour les caractères alphanumériques.</li> <li><strong>s</strong> pour les espaces, tabulation et tout autre caractère blanc.</li> </ul> <p>Si l'on souhaite tous les caractères qui ne sont pas dans une classe, on remplace la minuscule par une majuscule. Par exemple <strong>D</strong> indique tous ce qui n'est pas dans la classe chiffre</p> <p>Dans le code qui suit, la regex cherche 4 chiffres qui se suivent.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-3">Python</a></li> <li><a class="reference internal" href="#code-4">JS</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="n">match</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="s2">&quot;\d\d\d\d&quot;</span><span class="p">,</span> <span class="s2">&quot;2016&quot;</span><span class="p">)</span> <span class="k">print</span><span class="p">(</span><span class="n">match</span><span class="p">)</span> <span class="c1"># &lt;_sre.SRE_Match object; span=(0, 4), match='2016'&gt;</span> <span class="n">match</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="sa">r</span><span class="s2">&quot;\w\d&quot;</span><span class="p">,</span> <span class="s2">&quot;A4&quot;</span><span class="p">)</span> <span class="k">print</span><span class="p">(</span><span class="n">match</span><span class="p">)</span> <span class="c1"># &lt;_sre.SRE_Match object; span=(0, 2), match='A4'&gt;</span> </pre> </div> </div> <div class="tab-pane" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="nx">match</span> <span class="o">=</span> <span class="sr">/\d\d\d\d/</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span><span class="mi">2016</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">match</span><span class="p">);</span> <span class="c1">// [ '2016', index: 0, input: '2016' ] </span><span class="nx">match</span> <span class="o">=</span> <span class="sr">/\w\d/</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span><span class="s1">'A4'</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">match</span><span class="p">);</span> <span class="c1">// [ 'A4', index: 0, input: 'A4' ] </span> </pre> </div> </div> </div> <div class="section" id="definir-ses-propres-classes-dans-une-regex"> <h2>Définir ses propres classes dans une regex</h2> <p>Vous pouvez définir vos propres classes en plaçant les caractères possibles entre crochets.</p> <ul class="simple"> <li>[aeiouy] - Pour les voyelles en minuscule.</li> <li>[FGh] - Les lettres F, G ou h</li> </ul> <p>Si l'on souhaite le contraire dans un groupe de caractère, on le fait commencer par un accent circonflexe (^) .</p> <p>[^aeiouy] - Pour le groupe des consonnes.</p> <p>Il existe aussi une facilité d'écriture lorsque l'on définie un groupe.</p> <p>[a-m] - Toutes les lettres de 'a' à 'm'.</p> <p>Et si l'on souhaite que le caractère tiret - fasse partie du groupe, il faut le mettre à gauche du crochet fermant.</p> <p>[ze-] - Ce groupe contient les caractères. z, e et -</p> <p>Dans l'exemple ci-dessous, on cherche une chaine qui commence par FO suivi d'une voyelle.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-5">Python</a></li> <li><a class="reference internal" href="#code-6">JS</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-5"> <div class="highlight"> <pre class="literal-block"> <span class="n">match</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="s2">&quot;FO[^AEIOUY]&quot;</span><span class="p">,</span> <span class="s1">'FOO'</span><span class="p">)</span> <span class="k">print</span><span class="p">(</span><span class="n">match</span><span class="p">)</span> <span class="c1"># None</span> <span class="n">match</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="s2">&quot;FO[^AEIOUY]&quot;</span><span class="p">,</span> <span class="s1">'FOX'</span><span class="p">)</span> <span class="k">print</span><span class="p">(</span><span class="n">match</span><span class="p">)</span> <span class="c1"># &lt;_sre.SRE_Match object; span=(0, 3), match='FOX'&gt;</span> </pre> </div> </div> <div class="tab-pane" id="code-6"> <div class="highlight"> <pre class="literal-block"> <span class="nx">match</span> <span class="o">=</span> <span class="sr">/FO[^AEIOUY]/</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span><span class="s1">'FOO'</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">match</span><span class="p">);</span> <span class="c1">// null </span><span class="nx">match</span> <span class="o">=</span> <span class="sr">/FO[^AEIOUY]/</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span><span class="s1">'FOX'</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">match</span><span class="p">);</span> <span class="c1">// [ 'FOX', index: 0, input: 'FOX' ] </span> </pre> </div> </div> </div> </div> <div class="section" id="faire-de-la-validation"> <h2>Faire de la validation</h2> <p>Comme vu précédemment, test et search cherche le motif dans toute la chaîne, mais on peut modifier ce comportement en mettant un caractère <strong>^</strong> au début du motif ou le caractère <strong>$</strong> à la fin du motif.</p> <p>En utilisant ^ et $ on peut vérifier qu'une chaine de caractère dans sa totalité correspond à la regex.</p> <p>Par exemple pour vérifier qu'un utilisateur a saisi une date valide on peut utiliser ceci.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-7">JS</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-7"> <div class="highlight"> <pre class="literal-block"> <span class="kd">var</span> <span class="nx">regexDate</span> <span class="o">=</span> <span class="sr">/^[0-3]\d\/[01]\d\/\d\d\d\d$/</span><span class="p">;</span> <span class="nx">match</span> <span class="o">=</span> <span class="nx">regexDate</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span><span class="s2">&quot;19/12/3020&quot;</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">match</span><span class="p">);</span> <span class="c1">// [ '19/12/3020', index: 0, input: '19/12/3020' ] </span><span class="nx">match</span> <span class="o">=</span> <span class="nx">regexDate</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span><span class="s2">&quot;X19/12/3020&quot;</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">match</span><span class="p">);</span> <span class="c1">// null </span> </pre> </div> </div> </div> </div> <div class="section" id="les-quantificateurs"> <h2>Les quantificateurs</h2> <p>Il permet d'indiquer le nombre de fois qu'un caractère doit apparaitre avec la syntaxe suivante: {quantité} ou {quantité-min,quantité-max}.</p> <p>Exemple:</p> <ul class="simple"> <li>Hi{1,4} - H suivi de 1 à 4 i</li> <li>Hi{4} - H suivi de 4 i</li> <li>Hi{1,} - H suivi de 1 à une infinité de i</li> </ul> <p>Ainsi dans le motif représentant une date peut s'écrire comme ceci:</p> <p><pre> /^[0-3]\d\/[01]\d\/\d{4}$/</pre> </p> </div> <div class="section" id="arreter-la-recherche-des-que-possible"> <h2>Arrêter la recherche dès que possible</h2> <p>Un des problèmes qui se pose lorsque l'on a une regex qui contient un quantifier infinie et que celui-ci va essayer de prendre le plus de caractère possible alors que parfois, on souhaiterait qu'il s'arreter de prendre des caractères le plus tôt possible</p> <p>Par exemple, si je veux rechercher les balises html dans un texte et que j'utilise la regex suivante, le résulta risque d'être décevant.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-8">Python</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-8"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="sa">r</span><span class="s1">'&lt;.*&gt;'</span><span class="p">,</span> <span class="s1">'&lt;span&gt;foo&lt;/span&gt;&lt;p&gt;bar&lt;/p&gt;'</span><span class="p">)</span> <span class="o">&lt;</span><span class="n">_sre</span><span class="o">.</span><span class="n">SRE_Match</span> <span class="nb">object</span><span class="p">;</span> <span class="n">span</span><span class="o">=</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">26</span><span class="p">),</span> <span class="n">match</span><span class="o">=</span><span class="s1">'&lt;span&gt;foo&lt;/span&gt;&lt;p&gt;bar&lt;/p&gt;'</span><span class="o">&gt;</span> </pre> </div> </div> </div> <p>Comme vous pouvez le voir, toute la ligne est capturée. En fait .* va capturer des caractère tant qu'il le peut même les caractères '&lt;'. On dit que le quantifier est gourmand. Si l'on souhaite qu'il s'arrête le plus-tôt possible on va utiliser le caractère '?' après le quantifier.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-9">Python</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-9"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="sa">r</span><span class="s1">'&lt;.*?&gt;'</span><span class="p">,</span> <span class="s1">'&lt;span&gt;foo&lt;/span&gt;&lt;p&gt;bar&lt;/p&gt;'</span><span class="p">)</span> <span class="o">&lt;</span><span class="n">_sre</span><span class="o">.</span><span class="n">SRE_Match</span> <span class="nb">object</span><span class="p">;</span> <span class="n">span</span><span class="o">=</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">6</span><span class="p">),</span> <span class="n">match</span><span class="o">=</span><span class="s1">'&lt;span&gt;'</span><span class="o">&gt;</span> </pre> </div> </div> </div> <p>Si l'on souhaite itérer sur tous les résultats trouvés par une regex, en Python on utilisera la fonction <strong>finditer</strong>. En JavaScript lorsque l'on souhaite récupérer plusieurs résultas en JS il faut mettre un <strong>'g'</strong> à la fin de la regex définie puis utiliser la méthode exec.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-10">Python</a></li> <li><a class="reference internal" href="#code-11">JS</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-10"> <div class="highlight"> <pre class="literal-block"> <span class="n">regex</span> <span class="o">=</span> <span class="sa">r</span><span class="s1">'&lt;.*?&gt;'</span> <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">re</span><span class="o">.</span><span class="n">finditer</span><span class="p">(</span><span class="n">regex</span><span class="p">,</span> <span class="s1">'&lt;span&gt;foo&lt;/span&gt;&lt;p&gt;bar&lt;/p&gt;'</span><span class="p">):</span> <span class="k">print</span><span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="c1"># &lt;_sre.SRE_Match object; span=(0, 6), match='&lt;span&gt;'&gt;</span> <span class="c1"># &lt;_sre.SRE_Match object; span=(9, 16), match='&lt;/span&gt;'&gt;</span> <span class="c1"># &lt;_sre.SRE_Match object; span=(16, 19), match='&lt;p&gt;'&gt;</span> <span class="c1"># &lt;_sre.SRE_Match object; span=(22, 26), match='&lt;/p&gt;'&gt;</span> </pre> </div> </div> <div class="tab-pane" id="code-11"> <div class="highlight"> <pre class="literal-block"> <span class="kd">var</span> <span class="nx">regex</span> <span class="o">=</span> <span class="sr">/&lt;.*?&gt;/g</span> <span class="k">while</span> <span class="p">((</span><span class="nx">match</span> <span class="o">=</span> <span class="nx">regex</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span><span class="s1">'&lt;span&gt;foo&lt;/span&gt;&lt;p&gt;bar&lt;/p&gt;'</span><span class="p">))</span> <span class="o">!==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">match</span><span class="p">);</span> <span class="p">}</span> <span class="cm">/* [ '&lt;span&gt;', index: 0, input: '&lt;span&gt;foo&lt;/span&gt;&lt;p&gt;bar&lt;/p&gt;' ] [ '', index: 9, input: '&lt;span&gt;foo&lt;/span&gt;&lt;p&gt;bar&lt;/p&gt;' ] [ '&lt;p&gt;', index: 16, input: '&lt;span&gt;foo&lt;/span&gt;&lt;p&gt;bar&lt;/p&gt;' ] [ '&lt;/p&gt;', index: 22, input: '&lt;span&gt;foo&lt;/span&gt;&lt;p&gt;bar&lt;/p&gt;' ] */</span> </pre> </div> </div> </div> </div> <div class="section" id="capturer-des-morceaux-du-motif-avec-les-parentheses"> <h2>Capturer des morceaux du motif avec les parenthèses</h2> <p>On peut mettre des parties de la regex entre parenthèse pour définir ce que l'on appelle des groupes. On pourra par la suite récupérer les parties de la chaine de caractères qui correspondent aux groupes</p> <p>Par exemple prenons une date définie par la regex suivante</p> <p><pre> ^[0-3]\d\/[01]\d\/\d{4}$</pre> </p> <p>On va définir trois groupes comme ceci.</p> <p><pre> ^([0-3]\d)\/([01]\d)\/(\d{4})$</pre> </p> <p>Pour récupérer les éléments de la date saisie par un utilisateur on va faire comme ceci.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-12">Python</a></li> <li><a class="reference internal" href="#code-13">JS</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-12"> <div class="highlight"> <pre class="literal-block"> <span class="n">regex_date</span> <span class="o">=</span> <span class="sa">r</span><span class="s1">'^([0-3]\d)\/([01]\d)\/(\d{4})$'</span> <span class="n">matches</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">regex_date</span><span class="p">,</span> <span class="s2">&quot;11/10/2012&quot;</span><span class="p">)</span> <span class="k">print</span><span class="p">(</span><span class="n">matches</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span> <span class="c1"># 11</span> <span class="k">print</span><span class="p">(</span><span class="n">matches</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">2</span><span class="p">))</span> <span class="c1"># 10</span> <span class="k">print</span><span class="p">(</span><span class="n">matches</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">3</span><span class="p">))</span> <span class="c1"># 2012</span> </pre> </div> </div> <div class="tab-pane" id="code-13"> <div class="highlight"> <pre class="literal-block"> <span class="kd">var</span> <span class="nx">regexDate</span> <span class="o">=</span> <span class="sr">/^([0-3]\d)\/([01]\d)\/(\d{4})$/</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">matches</span> <span class="o">=</span> <span class="nx">regexDate</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span><span class="s2">&quot;11/10/2012&quot;</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">matches</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span> <span class="c1">// 11 </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">matches</span><span class="p">[</span><span class="mi">2</span><span class="p">]);</span> <span class="c1">// 10 </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">matches</span><span class="p">[</span><span class="mi">3</span><span class="p">]);</span> <span class="c1">// 2012 </span> </pre> </div> </div> </div> </div> <div class="section" id="nommer-les-groupes-capturants"> <h2>Nommer les groupes capturants</h2> <p>Cette fonctionnalité permet de récupérer le contenue d'un groupe en utilisant un nom plutôt qu'un indice. Elle n'ai pas disponible nativement en JS. En python, elle fonctionne comme ceci:</p> <p><pre> (?Pmotif)</pre> </p> <p>On met <strong>?P</strong> après la parenthèse ouvrante du groupe et on inscrit le nom du groupe entre chevrons $lt; $gt; Le motif ci-dessous permet d'utiliser les noms day, month et year pour récupérer les éléments de la date.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-14">Python</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-14"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="n">matches</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="sa">r</span><span class="s2">&quot;^(?P&lt;day&gt;[0-3]\d)/(?P&lt;month&gt;[01]\d)/(?P&lt;year&gt;\d{4})$&quot;</span><span class="p">,</span> <span class="s2">&quot;12/12/2004&quot;</span><span class="p">)</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">matches</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="s1">'day'</span><span class="p">)</span> <span class="s1">'12'</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">matches</span><span class="o">.</span><span class="n">groupdict</span><span class="p">()</span> <span class="p">{</span><span class="s1">'year'</span><span class="p">:</span> <span class="s1">'2004'</span><span class="p">,</span> <span class="s1">'month'</span><span class="p">:</span> <span class="s1">'12'</span><span class="p">,</span> <span class="s1">'day'</span><span class="p">:</span> <span class="s1">'12'</span><span class="p">}</span> </pre> </div> </div> </div> </div> <div class="section" id="utiliser-la-capture-d-un-groupe-au-sein-de-la-regex"> <h2>Utiliser la capture d'un groupe au sein de la regex</h2> <p>Grace à la syntaxe suivante (?P=nom-du-groupe) on peut dire ce que tu doit matché est ce que le groupe indiqué après '?P=' à capturé.</p> <p>Par exemple le motif suivant correspond à deux chiffres qui se suivent 11 22 33 44 ...</p> <p><pre> ^(?P\d)(?P=number)$</pre> </p> <p>L'exemple suivant correspond à n'importe quel caractère entouré par une simple quote ou une double quotes.</p> <p><pre> ^(?P['&quot;]).*(?P=quote)$</pre> </p> </div> <div class="section" id="avoir-une-condition-dans-sa-regex"> <h2>Avoir une condition dans sa regex</h2> <p>On peut rechercher un motif ou bien un autre uniquement si un group à capturé quelque chose. La syntaxe à utiliser est la suivante:</p> <p><pre> (?(nom-du-groupe)motif-ok|motif-ko)</pre> </p> <p>Par exemple, en Espagnol Si une phrase commence par un point d'interrogation retourné, elle doit finir par un point d'interrogation sinon elle finit par un point.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-15">Python</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-15"> <div class="highlight"> <pre class="literal-block"> <span class="n">regex</span> <span class="o">=</span> <span class="sa">r</span><span class="s2">&quot;(?P&lt;ponct&gt;¿)?.*(?(ponct)\?|\.)&quot;</span> <span class="n">match</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">regex</span><span class="p">,</span> <span class="s1">'¿Cómo estás?'</span><span class="p">)</span> <span class="k">print</span><span class="p">(</span><span class="n">match</span><span class="p">)</span> <span class="c1"># &lt;_sre.SRE_Match object; span=(0, 12), match='¿Cómo estás?'&gt;</span> <span class="n">match</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">regex</span><span class="p">,</span> <span class="s1">'Muy bien.'</span><span class="p">)</span> <span class="k">print</span><span class="p">(</span><span class="n">match</span><span class="p">)</span> <span class="c1"># &lt;_sre.SRE_Match object; span=(0, 9), match='Muy bien.'&gt;&lt;/ponct&gt;</span> </pre> </div> </div> </div> </div> <div class="section" id="rechercher-un-motif-qui-ne-soit-pas-precede-par-un-autre"> <h2>Rechercher un motif qui ne soit pas précédé par un autre</h2> <p>La syntaxe est la suivante, (?&lt;!motif-1)motif-2, se qui signifie motif-1 ne doit pas précéder motif-2</p> <p>Dans l'exemple ci-dessous, on capture uniquement les b qui ne sont pas précédés par un a.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-16">Python</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-16"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="s2">&quot;(?&lt;!a)b&quot;</span><span class="p">,</span> <span class="s2">&quot;ab cb&quot;</span><span class="p">)</span> <span class="o">&lt;</span><span class="n">_sre</span><span class="o">.</span><span class="n">SRE_Match</span> <span class="nb">object</span><span class="p">;</span> <span class="n">span</span><span class="o">=</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">),</span> <span class="n">match</span><span class="o">=</span><span class="s1">'b'</span><span class="o">&gt;</span> </pre> </div> </div> </div> <p>par exemple, si l'on veut récupèrer un texte entre quote mais qu'entre ces quote il peut y avoir des quotes précédé d'un . Comme dans 'l'ours' ou 'l'homme' on va utiliser le motif suivant:</p> <p><pre> '(.*?)(?&lt;!\\)'</pre> </p> <p>Qui ce lit: Une quote, n'importe qu'elle caractère 0 ou plusieurs fois. Une quote non précédé par un anti-slashe.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-17">Python</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-17"> <div class="highlight"> <pre class="literal-block"> <span class="k">for</span> <span class="n">match</span> <span class="ow">in</span> <span class="n">re</span><span class="o">.</span><span class="n">finditer</span><span class="p">(</span><span class="sa">r</span><span class="s2">&quot;'(.*?)(?&lt;!</span><span class="se">\\</span><span class="s2">)'&quot;</span><span class="p">,</span> <span class="sa">r</span><span class="s2">&quot;'l\'ours' 'l\'homme'&quot;</span><span class="p">):</span> <span class="k">print</span><span class="p">(</span><span class="n">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span> <span class="c1"># l\'ours</span> <span class="c1"># l\'homme</span> </pre> </div> </div> </div> </div> <div class="section" id="regarder-avant-d-y-mettre-les-pieds-ou-pas"> <h2>Regarder avant d'y mettre les pieds ou pas</h2> <p>Cette technique se nome <strong>lookahead</strong> elle consiste à dire je regarde si ce qui suit correspond au motif, mais je n'y vais pas. La syntaxe est la suivante (?=motif), on regarde si motif est présent.</p> <p>Par exemple la regex foo(?=d) validera la même chose que la regex food mais ne capturera pas ma même chose.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-18">Python</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-18"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="sa">r</span><span class="s2">&quot;foo(?=\d)&quot;</span><span class="p">,</span> <span class="s2">&quot;foo3&quot;</span><span class="p">)</span> <span class="o">&lt;</span><span class="n">_sre</span><span class="o">.</span><span class="n">SRE_Match</span> <span class="nb">object</span><span class="p">;</span> <span class="n">span</span><span class="o">=</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">3</span><span class="p">),</span> <span class="n">match</span><span class="o">=</span><span class="s1">'foo'</span><span class="o">&gt;</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="sa">r</span><span class="s2">&quot;foo\d&quot;</span><span class="p">,</span> <span class="s2">&quot;foo3&quot;</span><span class="p">)</span> <span class="o">&lt;</span><span class="n">_sre</span><span class="o">.</span><span class="n">SRE_Match</span> <span class="nb">object</span><span class="p">;</span> <span class="n">span</span><span class="o">=</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">4</span><span class="p">),</span> <span class="n">match</span><span class="o">=</span><span class="s1">'foo3'</span><span class="o">&gt;</span> </pre> </div> </div> </div> <p>Un des cas d'utilisation de lookahead et la vérification de la solidité d'un mot de passe.</p> <p>Vous regardez s'il contient une lettre, puis s'il contient un chiffre. Puis vous y allez en vérifiant qu'il fait au moins 6 caractères.</p> <p>re.search(r&quot;(?=.*[0-9].*)(?=.*[a-z].*)(.{6,}$)&quot;, &quot;aes320&quot;)</p> <p>Il existe également l'opérateur contraire qui valide si ce qui suit n'est pas le motif. La syntaxe est la suivante (?!motif), on regarde si motif n'est pas présent.</p> <p>Par exemple si l'on veut les mots qui commencent par foo suivi de n'importe quelle lettre majuscule sauf le B, J, M, T</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-19">Python</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-19"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="sa">r</span><span class="s2">&quot;foo(?![BJMT])[A-Z]&quot;</span><span class="p">,</span> <span class="s2">&quot;fooA&quot;</span><span class="p">)</span> <span class="o">&lt;</span><span class="n">_sre</span><span class="o">.</span><span class="n">SRE_Match</span> <span class="nb">object</span><span class="p">;</span> <span class="n">span</span><span class="o">=</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">4</span><span class="p">),</span> <span class="n">match</span><span class="o">=</span><span class="s1">'fooA'</span><span class="o">&gt;</span> </pre> </div> </div> </div> <p>Voila, j'espère que les techniques de regex vues dans cet article vous serons utiles. N'hésitez pas à partager vos cas d'usages et d'ici le prochain article à vos IDE et bon code <img alt="laughing" src="/theme/img/smiley/smiley-laughing.gif" /></p> </div> Utiliser Docker pour déployer un environnement de test2015-12-19T22:54:00+01:002015-12-19T22:54:00+01:00Vincent Mailloltag:autourducode.com,2015-12-19:/utiliser-docker-pour-deployer-un-environnement-de-test.html<p class="first last">Le but est d'utiliser Docker pour tester le fonctionnement d'une application web WSGI utilisant une base de données MySql</p> <p>Le but est d'utiliser Docker pour tester le fonctionnement d'une application web WSGI utilisant une base de données MySql. Docker est un logiciel qui gère des environnements isolés de l'OS, appelés containers, il les crée, les versionnent et facilite la communication entre eux. L'intérêt d'utiliser des containers et de bénéficier d'un environnement vierge à chaque lancement de test et d'éviter les effets de bord suite à des informations restées en BDD. Cela permet également de s'assurer que l'application est capable de fonctionner hors d'un environnement de développement. Dans cet article, on va utiliser Docker pour créer deux containers. L'un qui va héberger la base de données et l'autre notre application.</p> <div class="section" id="installation-de-docker-sur-debian-jessie"> <h2>Installation de Docker sur Debian Jessie</h2> <p>Enregistrez puis installez la clé du dépôt Docker.</p> <p><kbd class="kbd"> # apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D</kbd> </p> <p>Ajoutez le dépot docker à l'index des paquets.</p> <p><kbd class="kbd"> # echo &quot;deb https://apt.dockerproject.org/repo debian-jessie main&quot; &gt; /etc/apt/sources.list.d/docker.list</kbd> </p> <p>Mettez à jour.</p> <p><kbd class="kbd"> # apt-get update</kbd> </p> <p><kbd class="kbd"> # apt-get upgrate</kbd> </p> <p><kbd class="kbd"> # apt-get install docker-engine</kbd> </p> <p>Démarrez le daemon docker.</p> <p><kbd class="kbd"> # service docker start</kbd> </p> </div> <div class="section" id="creation-du-container-mysql"> <h2>Création du container MySQL</h2> <div class="section" id="recuperer-une-image-mysql-sur-docker-hub"> <h3>Récupérer une image MySQL sur Docker Hub</h3> <p>Comme je vous l'ai dit dans l'introduction, Docker permet de versionner des containers. On peut donc facilement récupérer l'image d'un container via un dépôt. A l'instar de GitHub pour le code source, <a class="reference external" href="https://hub.docker.com/">DocherHub</a> regroupe plusieurs dépôts de container.</p> <p>La commande docker pull permet de récupérer des images. On va lui passer en paramètre le nom du dépôt où sont stockés les images MySQL avec pour tag 5.7 qui est actuellement la dernière version de MySQL.</p> <p><kbd class="kbd"> # docker pull mysql:5.7</kbd> </p> <p>On va maintenant utiliser <strong>la commande docker run</strong> pour créer et lancer un container à partir de l'image.</p> <p><kbd class="kbd"> # docker run --name mysql_db -d -P -e MYSQL_ROOT_PASSWORD=root_password -e MYSQL_DATABASE=wsgi_database -e MYSQL_USER=wsgi_user -e MYSQL_PASSWORD=wsgi_password mysql:5.7</kbd> </p> <p>On utilise <strong>le paramètre --name</strong> pour donner comme nom à notre container mysql_db <strong>-d</strong> et pour le lancer en daemon <strong>-P</strong> permet de lier les ports exposés du container à des ports libre de l'hôte. Dans le cas du container MySQL, c'est le port 3306 qui est exposé. On définit via des variables le mot de passe administrateur et utilisateur puis le nom de l'utilisateur et de la base de données.</p> </div> </div> <div class="section" id="creation-du-container-de-l-application-wsgi"> <h2>Création du container de l'application WSGI</h2> <p>Voici notre application web. C'est une application WSGI qui affiche une citation qu'elle va récupérer dans une base de données MySQL en fonction de la date donnée dans l'URL. Pour fonctionner, notre application à besoin d'un fichier JSON dans lequel se trouve les paramètres de connexion à la base de données. Le module python qui contient notre application WSGI est également conçu pour peupler la base de donné lorsqu'il est exécuté directement.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">application.py</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="ch">#!/usr/bin/env python3</span> <span class="kn">import</span> <span class="nn">pymysql</span> <span class="kn">import</span> <span class="nn">os</span><span class="o">,</span> <span class="nn">sys</span> <span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span><span class="p">,</span> <span class="n">date</span> <span class="kn">import</span> <span class="nn">json</span> <span class="n">conf_file_name</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="vm">__file__</span><span class="p">),</span> <span class="s1">'conf.json'</span><span class="p">)</span> <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">conf_file_name</span><span class="p">)</span> <span class="k">as</span> <span class="n">conf_file</span><span class="p">:</span> <span class="n">CONF</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">conf_file</span><span class="p">)</span> <span class="k">def</span> <span class="nf">application</span><span class="p">(</span><span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">):</span> <span class="n">headers</span> <span class="o">=</span> <span class="p">[(</span><span class="s1">'Content-type'</span><span class="p">,</span> <span class="s1">'text/plain'</span><span class="p">)]</span> <span class="k">try</span><span class="p">:</span> <span class="n">date</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">strptime</span><span class="p">(</span> <span class="n">environ</span><span class="p">[</span><span class="s1">'PATH_INFO'</span><span class="p">],</span> <span class="s1">'/</span><span class="si">%d</span><span class="s1">-%m-%Y'</span><span class="p">)</span><span class="o">.</span><span class="n">date</span><span class="p">()</span> <span class="k">print</span><span class="p">(</span><span class="s2">&quot;DATE&quot;</span><span class="p">,</span> <span class="n">date</span><span class="p">)</span> <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span> <span class="n">start_response</span><span class="p">(</span><span class="s1">'404 Not Found'</span><span class="p">,</span> <span class="n">headers</span><span class="p">)</span> <span class="k">return</span> <span class="p">[</span><span class="s2">&quot;page not found&quot;</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s1">'utf-8'</span><span class="p">)]</span> <span class="k">try</span><span class="p">:</span> <span class="n">connection</span> <span class="o">=</span> <span class="n">pymysql</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span> <span class="n">host</span><span class="o">=</span><span class="n">CONF</span><span class="p">[</span><span class="s1">'host'</span><span class="p">],</span> <span class="n">port</span><span class="o">=</span><span class="n">CONF</span><span class="p">[</span><span class="s1">'port'</span><span class="p">],</span> <span class="n">user</span><span class="o">=</span><span class="n">CONF</span><span class="p">[</span><span class="s1">'user'</span><span class="p">],</span> <span class="n">passwd</span><span class="o">=</span><span class="n">CONF</span><span class="p">[</span><span class="s1">'passwd'</span><span class="p">],</span> <span class="n">db</span><span class="o">=</span><span class="n">CONF</span><span class="p">[</span><span class="s1">'db'</span><span class="p">])</span> <span class="k">with</span> <span class="n">connection</span><span class="o">.</span><span class="n">cursor</span><span class="p">()</span> <span class="k">as</span> <span class="n">cursor</span><span class="p">:</span> <span class="n">cursor</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s2">&quot;SELECT cit_text FROM citations WHERE cit_date = </span><span class="si">%s</span><span class="s2">&quot;</span><span class="p">,</span> <span class="n">date</span><span class="p">)</span> <span class="n">line</span> <span class="o">=</span> <span class="n">cursor</span><span class="o">.</span><span class="n">fetchone</span><span class="p">()</span> <span class="k">if</span> <span class="n">line</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span> <span class="n">status</span> <span class="o">=</span> <span class="s1">'404 Not Found'</span> <span class="n">body</span> <span class="o">=</span> <span class="s2">&quot;page not found&quot;</span> <span class="k">else</span><span class="p">:</span> <span class="n">status</span> <span class="o">=</span> <span class="s1">'200 OK'</span> <span class="n">body</span> <span class="o">=</span> <span class="n">line</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">except</span> <span class="n">pymysql</span><span class="o">.</span><span class="n">MySQLError</span> <span class="k">as</span> <span class="n">error</span><span class="p">:</span> <span class="n">status</span> <span class="o">=</span> <span class="s1">'500 Error'</span> <span class="n">body</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">error</span><span class="p">)</span> <span class="k">finally</span><span class="p">:</span> <span class="n">connection</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> <span class="n">start_response</span><span class="p">(</span><span class="n">status</span><span class="p">,</span> <span class="n">headers</span><span class="p">)</span> <span class="k">return</span> <span class="p">[</span><span class="n">body</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s1">'utf-8'</span><span class="p">)]</span> <span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span> <span class="n">connection</span> <span class="o">=</span> <span class="n">pymysql</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">host</span><span class="o">=</span><span class="n">CONF</span><span class="p">[</span><span class="s1">'host'</span><span class="p">],</span> <span class="n">port</span><span class="o">=</span><span class="n">CONF</span><span class="p">[</span><span class="s1">'port'</span><span class="p">],</span> <span class="n">user</span><span class="o">=</span><span class="n">CONF</span><span class="p">[</span><span class="s1">'user'</span><span class="p">],</span> <span class="n">passwd</span><span class="o">=</span><span class="n">CONF</span><span class="p">[</span><span class="s1">'passwd'</span><span class="p">],</span> <span class="n">db</span><span class="o">=</span><span class="n">CONF</span><span class="p">[</span><span class="s1">'db'</span><span class="p">])</span> <span class="k">with</span> <span class="n">connection</span><span class="o">.</span><span class="n">cursor</span><span class="p">()</span> <span class="k">as</span> <span class="n">cursor</span><span class="p">:</span> <span class="n">cursor</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s2">&quot;CREATE TABLE citations(cit_date DATE, cit_text VARCHAR(255), PRIMARY KEY(cit_date));&quot;</span><span class="p">)</span> <span class="n">sql</span> <span class="o">=</span> <span class="s2">&quot;INSERT INTO citations(cit_date, cit_text) VALUES (</span><span class="si">%s</span><span class="s2">, </span><span class="si">%s</span><span class="s2">)&quot;</span> <span class="n">cursor</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="n">sql</span><span class="p">,</span> <span class="p">(</span><span class="n">date</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">24</span><span class="p">),</span> <span class="s2">&quot;Joyeux Noel&quot;</span><span class="p">))</span> <span class="n">connection</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span> <span class="n">connection</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> </pre> </div> </div> </div> </div> <div class="section" id="creation-d-une-image-via-un-dockerfile"> <h2>Création d'une image via un Dockerfile</h2> <p>Pour hébergé notre application on va partir d'une image Debian sur lequel on va installer python 3. On va faire cela en utilisant un Dockerfile. C'est un fichier dans lequel on utilise des directives pour permettre à docker de créer une images. On utilisera ainsi la directive FROM pour donner une image de départ puis la commande RUN pour installer python3 et pip via apt-get. La commande COPY va nous permettre de copier le fichier application.py dans notre container. On pourrait également utiliser COPY pour copier le fichier JSON de configuration, mais le problème et que nous ne connaissons pas l'adresse IP de notre container MySQL ni le port sur lequel il est lié.</p> <p>Docker offre un moyen de connaitre l'adresse et le port d'un container à partir d'un autre. Il va lui transmettre ces informations via des variables d'environnements. Ces variables sont préfixées par le nom du container en majuscule suivi d'un underscore. Ainsi toutes les informations concernant notre container mysql_db seront accessibles dans d'autre container liée à celui-ci par des variables d'environnement commençant par MYSQL_DB_. Pour connaitre l'IP et le port lié au port exposé on utilisera respectivement les suffixe PORT_&lt;port-exposé&gt;_TCP_ADDR et DB_PORT_&lt;port-exposé&gt;_TCP_PORT où &lt;port-exposé&gt; sera remplacé par le numéro de port exposé.</p> <p>On va donc créer un script bash launch_sercer.sh qui lorsqu'il sera exécuté sur le container WSGI va créer le fichier conf.json en utilisant les variables d'environnements et lancer notre application python. Dans notre Dockerfile, on va utiliser l'instruction COPY pour copier launch_sercer.sh dans notre container. On va ensuite utiliser la directive CMD. CMD est un peu spéciale, il ne peut y en avoir qu'une seule dans un Dockerfile elle permet d'indiquer une commande qui sera exécutée à chaque lancement de notre container. On va lui demander d'exécuter notre script launch_server.sh</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-2">wsgi.dockerfile </a></li> <li><a class="reference internal" href="#code-3">launch_server.sh</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="k">FROM</span><span class="s"> debian:jessie</span> <span class="k">RUN</span> apt-get update <span class="o">&amp;&amp;</span> apt-get install -y python3 python3-pip <span class="k">RUN</span> pip3 install <span class="nv">PyMySQL</span><span class="o">==</span><span class="m">0</span>.6.7 COPY ./application.py ./ COPY ./launch_server.sh ./ <span class="k">CMD</span><span class="s"> [&quot;/bin/bash&quot;, &quot;launch_server.sh&quot;]</span> </pre> </div> </div> <div class="tab-pane" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="ch">#!/bin/bash </span> <span class="nb">echo</span> <span class="s1">'{&quot;host&quot;: &quot;'</span><span class="nv">$MYSQL_DB_PORT_3306_TCP_ADDR</span><span class="s1">'&quot;, '</span> <span class="se">\ </span> <span class="s1">'&quot;port&quot;: '</span><span class="nv">$MYSQL_DB_PORT_3306_TCP_PORT</span><span class="s1">', '</span> <span class="se">\ </span> <span class="s1">'&quot;passwd&quot; : &quot;'</span><span class="nv">$MYSQL_DB_ENV_MYSQL_PASSWORD</span><span class="s1">'&quot;, '</span><span class="se">\ </span> <span class="s1">'&quot;db&quot;: &quot;'</span><span class="nv">$MYSQL_DB_ENV_MYSQL_DATABASE</span><span class="s1">'&quot;, '</span><span class="se">\ </span> <span class="s1">'&quot;user&quot;: &quot;'</span><span class="nv">$MYSQL_DB_ENV_MYSQL_USER</span><span class="s1">'&quot;}'</span> &gt; conf.json cat conf.json python3 application.py python3 -c <span class="s2">&quot;from wsgiref.simple_server import make_server; \ from application import application; \ make_server('', 8000, application).serve_forever()&quot;</span> </pre> </div> </div> </div> <p>Maintenant que notre Dockerfile est terminé, on va exécuter la commande docker build pour créer notre image.</p> <p><kbd class="kbd"> # docker build -f wsgi.dockerfile .</kbd> </p> <p>La commande se termine en nous indiquant l'ID de l'image nouvellement créée.</p> <pre class="literal-block"> Sending build context to Docker daemon 20.48 kB ... Successfully built 0c9a5d5a0c4c </pre> </div> <div class="section" id="lier-notre-container-wsgi-au-container-mysql"> <h2>Lier notre container WSGI au container MySQL</h2> <p>On va ensuite lancer notre container en utilisant la commande run. <strong>Le paramètre --link</strong> avec la valeur mysql_db va indiquer à docker qu'il doit créer à l'intérieure du nouveau container, des variables d'environnement concernant le container mysql_db.</p> <p><kbd class="kbd"> # docker run --name wsgi -p 127.0.0.1:80:8000 --link mysql_db 0c9a5d5a0c4c</kbd> </p> <p>A la place du paramètre <strong>-P</strong> on a utilisé <strong>-p</strong> pour donner le port de l'hôte sur lequel lier le port exposé de container. Maintenant, vous pouvez directement jeter un coup d'oeil avec votre navigateur à l'adresse 127.0.0.1/24-12-2015. Après avoir vérifié que tout fonctionne, vous pourrez arrêter et supprimer vos containers avec les commandes.</p> <p><kbd class="kbd"> # docker stop wsgi mysql_db</kbd> <kbd class="kbd"> # docker rm wsgi mysql_db</kbd> </p> <p>Sans supprimer les containers, impossible de recréer d'autre avec le même nom. Vous allez voir que grâce au système de cache de docker, la création de container prend beaucoup moins de temps la deuxième fois.</p> </div> <div class="section" id="l-automatisation"> <h2>L'automatisation</h2> <p>Bon c'est bien beau, mais pour le moment, ça nous simplifie pas la vie, on va donc faire un bash pour automatiser tout ça. Il y a plusieurs astuces la première, c'est de quitter le bash dès que quelque chose échoue. Il n'est pas utile, de lancer le container wsgi si celui hébergeant la BDD crash. on va donc utiliser le pattern suivant après chaque commande:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-4">bash</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="k">if</span> <span class="o">[</span> <span class="nv">$?</span> !<span class="o">=</span> <span class="m">0</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span> <span class="nb">exit</span> <span class="m">1</span> <span class="k">fi</span> </pre> </div> </div> </div> <p>La deuxième astuce, c'est de stopper les containers déjà lancés et de les supprimer avant de quitter le bash. Sans cela on ne pourra lancer le bash une seconde fois car il y aura un conflit avec les noms des containers. Pour ne pas écrire en dur les container à supprimer selon le succès ou l'échec d'une étape, on va écrire une fonction de nettoyage.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-5">bash</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-5"> <div class="highlight"> <pre class="literal-block"> <span class="k">function</span> clean_container <span class="o">{</span> <span class="nv">names</span><span class="o">=</span><span class="s2">&quot;^(mysql_db|wsgi)</span>$<span class="s2">&quot;</span> docker ps --format <span class="s2">&quot;{{.Names}}&quot;</span> -f <span class="nv">status</span><span class="o">=</span>running <span class="p">|</span> grep -P <span class="s2">&quot;</span><span class="nv">$names</span><span class="s2">&quot;</span> <span class="p">|</span> xargs -I % docker stop % docker ps --format <span class="s2">&quot;{{.Names}}&quot;</span> -f <span class="nv">status</span><span class="o">=</span>exited <span class="p">|</span> grep -P <span class="s2">&quot;</span><span class="nv">$names</span><span class="s2">&quot;</span> <span class="p">|</span> xargs -I % docker rm % <span class="o">}</span> </pre> </div> </div> </div> <p>On utilise dans cette fonction la commande <strong>docker ps</strong> pour lister les containers le <strong>paramètre status</strong> nous permet de lister seulement ceux qui sont en fonctionnement ou arrêté. Docker ps propose une option <strong>-f name=valeur</strong> pour filtrer sur les noms des container. Malheureusement cette option liste tous les containers dont le nom contient la valeur. Pour lister les container sur le nom exacte, on va utiliser grep. <strong>L'option --format</strong> nous permet d'afficher seulement le nom.</p> <p>La troisième astuce qui est l'un des plus important, lorsque docker vous dit qu'il vient de démarrer un container avec succès rien ne vous prouve que le serveur dans le container et près à recevoir des requêtes. On va donc mettre un sleep après chaque docker run pour laisser le temps au serveur d'être à l'écoute du réseau. Une autre solution et d'utiliser la commande wget avec l'option spider pour ne pas télécharger la page.</p> <p><kbd class="kbd"> $ wget --tries 10 --spider -q --retry-connrefused &lt;address-ip&gt;:&lt;port&gt;</kbd> </p> <p>Si vous prenez cette option, vous pouvez récupérer le port de la machine mysql_db via la commande</p> <p><kbd class="kbd"> # docker inspect --format '{{ (index (index .NetworkSettings.Ports &quot;3306/tcp&quot;) 0).HostPort }}' mysql_db</kbd> </p> <p>Voici le bash final. J'ai mis un simple curl à l'endroit où il faut appeler votre framework de test</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-6">launch_test.sh</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-6"> <div class="highlight"> <pre class="literal-block"> <span class="ch">#!/bin/bash </span> <span class="k">function</span> clean_container <span class="o">{</span> <span class="nv">names</span><span class="o">=</span><span class="s2">&quot;^(mysql_db|wsgi)</span>$<span class="s2">&quot;</span> docker ps --format <span class="s2">&quot;{{.Names}}&quot;</span> -f <span class="nv">status</span><span class="o">=</span>running <span class="p">|</span> grep -P <span class="s2">&quot;</span><span class="nv">$names</span><span class="s2">&quot;</span> <span class="p">|</span> xargs -I % docker stop % docker ps --format <span class="s2">&quot;{{.Names}}&quot;</span> -f <span class="nv">status</span><span class="o">=</span>exited <span class="p">|</span> grep -P <span class="s2">&quot;</span><span class="nv">$names</span><span class="s2">&quot;</span> <span class="p">|</span> xargs -I % docker rm % <span class="o">}</span> docker run --name mysql_db -d -P -e <span class="nv">MYSQL_ROOT_PASSWORD</span><span class="o">=</span>root_password -e <span class="nv">MYSQL_DATABASE</span><span class="o">=</span>wsgi_database -e <span class="nv">MYSQL_USER</span><span class="o">=</span>wsgi_user -e <span class="nv">MYSQL_PASSWORD</span><span class="o">=</span>wsgi_password mysql:5.7 <span class="k">if</span> <span class="o">[</span> <span class="nv">$?</span> !<span class="o">=</span> <span class="m">0</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span> exit<span class="p">;</span> <span class="k">else</span> <span class="nb">echo</span> <span class="s2">&quot;mysql_db running&quot;</span> <span class="k">fi</span> <span class="nv">output</span><span class="o">=</span><span class="k">$(</span>mktemp<span class="k">)</span> <span class="nb">echo</span> <span class="s1">'tmpfile'</span> <span class="nv">$output</span> docker build -f wsgi.dockerfile . &gt; <span class="s2">&quot;</span><span class="nv">$output</span><span class="s2">&quot;</span> <span class="k">if</span> <span class="o">[</span> <span class="nv">$?</span> !<span class="o">=</span> <span class="m">0</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span> rm <span class="nv">$output</span> clean_container <span class="nb">exit</span> <span class="m">1</span> <span class="k">else</span> <span class="nb">echo</span> <span class="s2">&quot;WSGI built&quot;</span> <span class="k">fi</span> <span class="nv">wsgi_img_id</span><span class="o">=</span><span class="k">$(</span>grep <span class="s2">&quot;Successfully built&quot;</span> <span class="s2">&quot;</span><span class="nv">$output</span><span class="s2">&quot;</span> <span class="p">|</span> cut -d <span class="s1">' '</span> -f <span class="m">3</span><span class="k">)</span> <span class="nv">mysql_port</span><span class="o">=</span><span class="k">$(</span>docker inspect --format <span class="s1">'{{ (index (index .NetworkSettings.Ports &quot;3306/tcp&quot;) 0).HostPort }}'</span> mysql_db<span class="k">)</span> sleep <span class="m">10</span> docker run -d --name wsgi -p <span class="m">127</span>.0.0.1:80:8000 --link mysql_db <span class="nv">$wsgi_img_id</span> <span class="k">if</span> <span class="o">[</span> <span class="nv">$?</span> !<span class="o">=</span> <span class="m">0</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span> rm <span class="nv">$output</span> clean_container <span class="nb">exit</span> <span class="m">1</span><span class="p">;</span> <span class="k">else</span> <span class="nb">echo</span> <span class="s2">&quot;WSGI running&quot;</span> <span class="k">fi</span> sleep <span class="m">10</span> curl <span class="m">127</span>.0.0.1/24-12-2015 <span class="p">|</span> grep <span class="s1">'Joyeux Noel'</span> <span class="k">if</span> <span class="o">[</span> <span class="nv">$?</span> <span class="o">==</span> <span class="m">0</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span> <span class="nb">echo</span> TEST OK<span class="p">;</span> clean_container <span class="k">else</span> <span class="nb">echo</span> TEST FAILD<span class="p">;</span> clean_container <span class="nb">exit</span> <span class="m">1</span> <span class="k">fi</span> </pre> </div> </div> </div> <p>Voilou j'espère que vous avez aimé cet article alor à vos IDE et bon code <img alt="laughing" src="/theme/img/smiley/smiley-laughing.gif" /></p> </div> Philosophie des tests unitaires2015-11-07T08:36:00+01:002015-11-07T08:36:00+01:00Vincent Mailloltag:autourducode.com,2015-11-07:/philosophie-des-tests-unitaires.html<p class="first last">Si aujourd'hui il existe une pléthore de frameworks pour nous aider à écrire des tests unitaires. Encore faut-il savoir comment tester.</p> <p>Si aujourd'hui il existe une pléthore de frameworks pour nous aider à écrire des tests unitaires. Encore faut-il savoir comment tester.</p> <p>Avant de commencer cet article il me semble important de rappeler quelque base sur ce qu'est un logiciel écrit avec des principes orientés objets et donc de rappeler ce qu'est un objet.</p> <p>Un objet est un ensemble qui contient des données. La valeur des données définissent l'état de l'objet. Pour que l'état soit toujours cohérent, on ne peut modifier directement les données mais, on utilise des méthodes définies dans l'objet qui le font à notre place. C'est le principe d'encapsulation. Toute la mécanique est cachée dans l'objet</p> <p>Si vous avez un objet mixeur, vous allez avoir trois méthodes.</p> <ul class="simple"> <li>Mettre les aliments dedans</li> <li>Mixer</li> <li>Récupérer les aliments</li> </ul> <p>L'ensemble des choses que l'on peut faire avec un objet s'appelle son <strong>interface</strong>. La façon dont l'objet réalise ces choses s'appelle son <strong>implémentation</strong>. Ainsi, l'interface répond à la question que fait-il et l'implémentation à la question comment le fait-il. La plupart des langages de programmation ne font pas de séparation stricte entre interface et implémentation, ces deux concepts sont regroupés dans la classe.</p> <p>Programmer pour une interface et non une classe. Quand vous programmez vous ne devez pas vous demander comment fonctionne cet objet mais, que puis-je faire avec ? S'il répond à mes besoins, je peux l'utiliser...</p> <p>C'est pareil lorsque vous testez un objet. Regardez son interface et testez si son implémentation respect son interface.</p> <p>Si je vous donne un mixeur à tester mettez des aliments dedans, mixer, récupérer les aliments et vérifiez que vos aliments sont bien mixés. C'est ce que l'on appelle le cas autoroute c'est le cas d'utilisation le plus normal que l'on puisse faire pour faire de bon testes, il faut malmener votre objet. En s'intéressant uniquement à l'interface du mixeur, on peut faire les tests suivants:</p> <ul class="simple"> <li>Vérifier que si je mets mes aliments dedans et que je ne le lance pas je puisse récupérer mes aliments non mixés</li> <li>Vérifier que si je lance mon mixeur à vide, je récupère du vide à la fin.</li> </ul> <p>Ce qu'il ne faut surtout pas faire, vérifier que lorsque je lance mon mixeur, cela entraine son moteur électrique qui va entrainer la lame.</p> <p>L'interface de l'objet mixeur ne vous dis pas que l'on peut regarder son moteur elle ne dit même pas qu'il a un moteur. Beaucoup de langages permettent de rendre inaccessible tout ce qui ne fait pas parti de l'interface grâce au scope privé. Ce n'est pas le cas de python même si une convention existe, faire commencer par un _ tout ce qui ne fait pas partie de l'interface.</p> <div class="section" id="probleme-de-composition"> <h2>Problème de composition</h2> <p>En programmation orientée objet, il y a l'instanciation, l'étape où l'on va construire un objet.l'instanciation fait parti de l'interface de l'objet, (sauf cas très particulier des classes imbriqués),Ainsi on pourrait avoir un mixeur qui s'instancie avec un moteur. Ce qui permet d'avoir des mixeurs avec des moteurs de différentes puissances. L'interface d'un moteur est très simple. démarrer arrêter</p> <p>Concernant les testes de notre mixeur il va falloir les modifier pour que lorsque l'on construise un mixeur on lui passe un moteur mais quel moteur choisir ? Doit-on tester notre mixeur avec tous les moteurs ? <img alt="undecided" src="/theme/img/smiley/smiley-undecided.gif" /> Non du moment que l'on a testé que l'implémentation de nos moteurs respectent leur interface et que l'on a programmé notre mixeur pour qu'il fonctionne avec l'interface d'un moteur, pas de problème. <img alt="cool" src="/theme/img/smiley/smiley-cool.gif" /></p> <p>Donc on en choisit un au pif ?</p> <p>On peut également utiliser un simulacre. Les simulacres ou mock sont des objets utilisés dans les tests qui sont censés respecter l'interface de l'objet simulés, ils n'ont pas grosse intelligence et l'on fait généralement en sorte que les appels à une de leur méthode retourne une valeur fixe.</p> <p>Vous étes le héro de ce test: <a class="reference internal" href="#j-utilise-des-mocks">J'utilise des mocks</a> ou <a class="reference internal" href="#j-utilise-un-moteur-au-pif">J'utilise un moteur au pif</a> pour les tests, à vous de choisir.</p> </div> <div class="section" id="j-utilise-des-mocks"> <h2>J'utilise des mocks</h2> <p>Créons un simulacre de moteur puis on va vérifier Que lorsqu'on lance le mixeur, la méthode démarrer du moteur est appelée une fois. Puisque lorsqu'on appelle la méthode récupérer les aliments du mixeur, la méthode arrêter du moteur est appelée.</p> <p>Sur les 10 implémentations de mixeurs que vous avez testé 2 ne passe pas. L'un des mixeurs lance et arrête 3 fois le moteur pour permettre aux aliments de retomber et de mieux être mixée.</p> <p>L'autre mixeur n'appelle pas la méthode, arrêter, à l'ouverture du mixeur il l'appelle une seconde après avoir lancé le moteur. Il met deux secondes à ouvrir le mixeur pour être sûr que le moteur soit arrêté.</p> <p>Vous adaptez vos testes pour ces deux mixeurs vous vous rendez compte que vos testes ne sont plus fait pour une interface, mais une implémentation en effet vous avez du vous poser la question comment marche mon mixeur pour pouvoir adapter vos teste. Est-ce vraiment mal ? Une malédiction va-t-elle s'abattre sur vous ? Peut importe maintenant que vos teste passe vous n'avez plus le temps de revenir en arrière. Allez au paragraphe <a class="reference internal" href="#le-programme-evolue">Le programme évolue</a></p> </div> <div class="section" id="j-utilise-un-moteur-au-pif"> <h2>J'utilise un moteur au pif</h2> <p>Vous prenez un moteur au pif pour chacune des dix implémentations du mixeur. Vos tests passent, tout va pour le mieux dans le meilleur des mondes. Allez au paragraphe <a class="reference internal" href="#le-programme-evolue">Le programme évolue</a></p> </div> <div class="section" id="le-programme-evolue"> <h2>Le programme évolue</h2> <p>Petit à petit vous forgé votre expérience en écriture de testes vos moteurs sont utilisés dans deux autres types d'objets, des machines à laver et des sèches linges. On vous demande de concevoir un ventilateur avec deux vitesse différentes. On souhaite que le moteur est 3 états:</p> <ul class="simple"> <li>arrêté</li> <li>lent</li> <li>rapide</li> </ul> <p>Vous adaptez les implémentations de vos moteurs et les testes unitaires de ceux-ci. Rendez-vous au paragraphe <a class="reference internal" href="#j-ai-utilise-des-mock">J'ai utilisé des mock</a> ou <a class="reference internal" href="#j-ai-utilise-un-moteur-au-pif">J'ai utilisé un moteur au pif</a> en fonction de votre choix précédent.</p> </div> <div class="section" id="j-ai-utilise-des-mock"> <h2>J'ai utilisé des mock</h2> <p>vous relancez l'ensemble de vos tests unitaires et tout est au vers vous ne voyez pas l'armée de bugs arrivés sournoisement.</p> <p>Aucun des mixeurs avec les nouveaux moteurs ne fonctionne, vous n'avez pas pensé à adapter vos simulacres. Le langage de scripte utilisé n'indiquant pas de façon formel que vos simulacres doivent respecter l'interface de l'objet qui simule, quand l'interface change les mocks n'en savent rien et les bugs arrivent. Rendez-vous au paragraphe <a class="reference internal" href="#fin-de-l-aventure">Fin de l'aventure</a></p> </div> <div class="section" id="j-ai-utilise-un-moteur-au-pif"> <h2>J'ai utilisé un moteur au pif</h2> <p>vous relancez l'ensemble de vos tests unitaires et remarquez que tout est au rouge sauf les tests sur les moteurs. Vos tests vous indiques qu'aucun mixeur ne peut comprendre la nouvelle interface des moteurs. Vous modifiez tous vos mixeurs et relancez les tests, tous est au vert. Rendez-vous au paragraphe <a class="reference internal" href="#fin-de-l-aventure">Fin de l'aventure</a></p> </div> <div class="section" id="fin-de-l-aventure"> <h2>Fin de l'aventure</h2> <p>Comme on peut le voir ici, une des principales plus-value des tests unitaires et de se prémunir contre les régressions et de connaitre l'impact d'une modification sur les autres composants. Il faut cependant garder à l'esprit que les tests peuvent prouver la présence de bugs, mais pas l'absence.</p> </div> <div class="section" id="la-quete-de-la-couverture-de-code-en-pervertie-plus-d-un"> <h2>La quête de la couverture de code en pervertie plus d'un</h2> <p>La couverture de code, c'est la quantité d'instruction dans le code qui est exécuté lorsque les testes tournent.</p> <p>Si toutes les instructions sont exécutées, vous avez une couverture de cent pour cent. Il existe des logiciels qui permettent de connaitre cette couverture comme coverage pour python ou cobertura pour Java. Ces logiciels marchent très bien mais, utiliser leur métrique peut amener à certaine dérive, en effet ce n'est pas parce qu'un code à une bonne couverture de tests que les tests sont pertinents. Voici un exemple pour vous en convaincre avec la méthode faire ses courses:</p> <pre class="literal-block"> class: Humain methode: FaireSesCources(magasin) this.entrerDansMagasin(magasin) this.sortirDuMagasin(magasin) methode: entrerDansMagasin(magasin) du code... methode: sortirDuMagasin(magasin) encore du code ... </pre> <p>Je me fixe pour seul objectif les 100% une façon très simple de les atteindre est la suivante.</p> <pre class="literal-block"> class: TestHumain: homme: h1, h2 magazin: m1, m2 h1.FaireSesCource(m1) h2.entrerDansMagasin() h2.sortirDuMagasin() h1 == h2 ? </pre> <p>Voila, chaque instruction est même appelé deux fois par ce test, mais la pertinence est lamentable. Un test pertinent aurai été de vérifier si h1 à moins d'argent est plus de provision après avoir fait ses courses. De plus si une modification sur entrerDansMagasin ou sortirDuMagasin incorpore un bug, cette régression ne sera pas détectée et pourtant la couverture ne pouvait pas être meilleure.</p> <p>Autre chose, votre code est semblable à un graphe ou chaque structure conditionnelle ou boucle représente un noeud du graphe.</p> <pre class="literal-block"> if a: # instruction 1 if b: # instruction 2 </pre> <p>Ici un test avec &quot;a&quot; et &quot;b&quot; à vrai couvre toute les instructions, mais que se passe-t-il si l'on exécute seulement l'instruction 2 sans avoir exécuté l'instruction 1 ? Avoir une couverture totale du code ne garantit pas que l'on est passé par tous les noeuds du graphe, mais pas que l'on a testé tous les chemins possibles. Donc ne vous laissez pas impressionner par cette métrique qui n'est pas une preuve de la qualité du code, mais inquiétez vous si elle est au ras des pâquerettes, c'est le signe que des régressions passeront inaperçue.</p> </div> <div class="section" id="tout-tester-n-a-pas-d-interet"> <h2>Tout tester n'a pas d'intérêt</h2> <p>Ne perdez pas votre temps à tester des constantes. Il n'y a aucune logique, c'est simplement du déclaratif écrire des tests pour ça est tellement stupide qu'un script awk pourrait faire le job à votre place. Sérieusement ? Vous allez vraiment copier le retour constant d'une méthode pour le coller dans un test ? Si vous avez du temps à perdre, écrivez deux fois votre programme et faites un diff se sera encore mieux. Evitez également d'être trop tatillons avec les testes si vous testez une sortie ou un affichage, vérifié seulement que les informations qui doivent être affichées sont présentes. Rien de plus pénible que des tests qui pètent dès que l'on ajoute une pauvre virgule ou un soulignage.</p> </div> <div class="section" id="les-tests-en-dos-a-dos"> <h2>Les tests en dos à dos</h2> <p>Une dernière astuce, pour tester les algorithmes, souvent on oublie un cas particulier dans lequel l'algo ne marche pas. Il existe une technique particulière pour vous aider face à cette problématique, le dos à dos. Elle consiste à créer un autre algorithme que fait la même chose que l'algorithme à tester, mais de la façon la plus stupide qui soit, peu importe que ses performances soient désastreuses. Par exemple, un algorithme stupide de collision entre deux formes géométriques prend tous les points un à un de la forme 1 et regarde s'ils correspondent à un point dans la forme 2.</p> <p>Notre test va générer des entrées aléatoires et les donner à manger à nos deux algorithmes puis comparer les deux sorties. il s'arrête à chaque erreur. On regarde quelles données d'entrées ont provoqué l'arrêt du test puis on crée un test spécifique avec ce cas particulier. On corrige notre algorithme et on recommence. Ca permet de tester des choses complexes avec peu d'effort.</p> <p>Voilou j'espère qu'après ça, vous aurez une idée plus claire sur les pièges à éviter lors de l'écriture de tests, alors vos IDE et bon code <img alt="laughing" src="/theme/img/smiley/smiley-laughing.gif" /></p> </div> Le bytecode python2015-09-30T13:52:00+02:002015-09-30T13:52:00+02:00Vincent Mailloltag:autourducode.com,2015-09-30:/le-bytecode-python.html<p class="first last">En Python, les programmes avant d'être interprétés sont pré-compillé en <strong>bytecode</strong>. Dans cet article, nous allons voir comment on peut lire le bytecode d'une fonction et le modifier. Il est parfois intéressant de visualiser le bytecode d'une fonction, mais avant de se lancer dans une telle opération, il faut savoir comment est fait un callable python.</p> <p>En Python, les programmes avant d'être interprétés sont pré-compillé en bytecode. Dans cet article, nous allons voir comment on peut lire le bytecode d'une fonction et le modifier. Il est parfois intéressant de visualiser le bytecode d'une fonction, mais avant de se lancer dans une telle opération, il faut savoir comment est fait un callable python.</p> <div class="section" id="les-objets-de-type-code"> <h2>Les objets de type code</h2> <p>Toutes méthodes ou fonctions contiennent un objet de type code. Que l'on peut récupérer via l'attribut <strong>__code__</strong>. Cet objet contient plusieurs choses.</p> <table border="1" class="docutils"> <colgroup> <col width="12%" /> <col width="88%" /> </colgroup> <tbody valign="top"> <tr><td>co_flags</td> <td><div class="first last line-block"> <div class="line">Entier qui indique si la fonction utilise *args ou **kwargs ou s'il y a des imports avec __future__</div> <div class="line">0x04: vous utilisez *args</div> <div class="line">0x08: vous utilisez **kwargs</div> </div> </td> </tr> <tr><td>co_code</td> <td>Le bytecode de la fonction</td> </tr> <tr><td>co_name</td> <td>Le nom de la fonction</td> </tr> <tr><td>co_filename</td> <td>Le nom du module dans lequel est déclaré la fonction</td> </tr> <tr><td>co_firstlineno</td> <td>Le numéro de la première ligne de la fonction</td> </tr> <tr><td>co_lnotab</td> <td><div class="first line-block"> <div class="line">Tableau compressé de correspondance entre l'instruction en bytecode et le numéro de ligne de l'instruction Il se forme par une suite de couple le premier membre du couple et l'indice de l'instruction du bytecode la deuxième partie est la ligne du code source correspondante. Par contre seule les incréments entre les indices sont stockés dans les couples.</div> <div class="line">Ainsi pour coder le tableau de correspondance suivant:</div> </div> <table border="1" class="docutils"> <colgroup> <col width="43%" /> <col width="10%" /> <col width="38%" /> <col width="10%" /> </colgroup> <thead valign="bottom"> <tr><th class="head">Indice de l'instruction dans le bytecode</th> <th class="head">Incrément</th> <th class="head">Numéro de ligne dans le code source</th> <th class="head">Incrément</th> </tr> </thead> <tbody valign="top"> <tr><td>0</td> <td>0</td> <td>1</td> <td>1</td> </tr> <tr><td>3</td> <td>3</td> <td>1</td> <td>0</td> </tr> <tr><td>6</td> <td>3</td> <td>10</td> <td>10</td> </tr> <tr><td>42</td> <td>39</td> <td>12</td> <td>2</td> </tr> </tbody> </table> <p>lnotab sera:</p> <p><pre> lnotab = byte(zip((0, 3, 3, 39), (1,0,10,2)))</pre> </p> <p class="last">L'autre chose à savoir est que l'on prend pour indice de l'instruction en bytecode l'indice de la première instruction de chaque ligne du code source.</p> </td> </tr> <tr><td>co_varnames</td> <td>Un tuple avec le nom des variables utilisées dans la fonction y compris le nom des arguments de la fonction</td> </tr> <tr><td>co_nlocals</td> <td>Le nombre de variables locales utilisé par la fonction</td> </tr> <tr><td>co_consts</td> <td>Tuple des constantes utilisées dans la fonction</td> </tr> <tr><td>co_names</td> <td>Le nom des variables définies à l'extérieur de la portée de la fonction</td> </tr> <tr><td>co_argcount</td> <td>Le nombre d'arguments positionnels que prend la fonction</td> </tr> <tr><td>co_kwonlyargcount</td> <td>Le nombre d'arguments uniquement utilisables par mot-clé que prend la fonction</td> </tr> <tr><td>co_freevars</td> <td>Pour les closures, tuple avec les noms des variables définies dans la fonction parent</td> </tr> <tr><td>co_cellvars</td> <td>Tuple du nom des variables définies dans la fonction est référencées dans une closure</td> </tr> <tr><td>co_stacksize</td> <td>La taille de la pile</td> </tr> </tbody> </table> </div> <div class="section" id="lire-le-bytecode-d-une-fonction"> <h2>Lire le bytecode d'une fonction</h2> <p>Pour exemple, on va prendre une petite fonction qui traduit les chiffres en lettre en entier</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="k">def</span> <span class="nf">translate</span><span class="p">(</span><span class="n">number</span><span class="p">):</span> <span class="n">word_to_digit</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;one&quot;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="s2">&quot;two&quot;</span><span class="p">:</span> <span class="mi">2</span><span class="p">}</span> <span class="k">return</span> <span class="n">word_to_digit</span><span class="p">[</span><span class="n">number</span><span class="p">]</span> <span class="nb">print</span><span class="p">(</span><span class="n">translate</span><span class="o">.</span><span class="vm">__code__</span><span class="o">.</span><span class="n">co_consts</span><span class="p">)</span> <span class="c1"># (None, 1, 'one', 2, 'two')</span> <span class="nb">print</span><span class="p">(</span><span class="n">translate</span><span class="o">.</span><span class="vm">__code__</span><span class="o">.</span><span class="n">co_varnames</span><span class="p">)</span> <span class="c1"># ('number', 'word_to_digit')</span> <span class="nb">print</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="n">translate</span><span class="o">.</span><span class="vm">__code__</span><span class="o">.</span><span class="n">co_code</span><span class="p">))</span> <span class="c1"># [105, 2, 0, 100, 1, 0, 100, 2, 0, 54, 100, 3, 0, 100, 4, 0, 54, 125, 1, 0, 124, 1, 0, 124, 0, 0, 25, 83]</span> </pre> </div> </div> </div> <p>La fonction translate possède cinq constantes &quot;one&quot;, &quot;two&quot;, 1 et 2 ainsi que None, cela peut sembler étrange, mais None est toujours ajouté à la liste des constantes utilisées par les fonctions. Cela est dû au fait qu'une fonction retourne None par défaut et que l'interpréteur pour une raison d'efficacité ne teste pas si la fonction à des instructions return à chaque sorti de la fonction. La fonction utilise deux variables 'number' et 'word_to_digit'. Si l'on affiche le bytecode, on se retrouve avec un objet byte illisible que l'on peut changer en liste d'entiers, mais ce n'est pas forcément mieux.</p> <p>Le mieux pour voir le bytecode d'une fonction est d'utiliser <strong>le module dis</strong> qui va désassembler la fonction.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-2">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="kn">import</span> <span class="nn">dis</span> <span class="n">dis</span><span class="o">.</span><span class="n">dis</span><span class="p">(</span><span class="n">translate</span><span class="p">)</span> </pre> </div> </div> </div> <p>Ainsi la fonction translate désassemblée se présente comme ceci:</p> <pre class="literal-block"> 16 0 BUILD_MAP 2 3 LOAD_CONST 1 (1) 6 LOAD_CONST 2 ('one') 9 STORE_MAP 17 10 LOAD_CONST 3 (2) 13 LOAD_CONST 4 ('two') 16 STORE_MAP 17 STORE_FAST 1 (word_to_digit) 18 20 LOAD_FAST 1 (word_to_digit) 23 LOAD_FAST 0 (number) 26 BINARY_SUBSCR 27 RETURN_VALUE </pre> <p>La première colonne est le numéro de la ligne de l'instruction comme vous avez pu le deviner, chez moi translate.__code__.co_firstlineno == 15. La deuxième colonne indique la position de l'instruction dans le bytecode. La troisième colonne contient les instructions en anglais plus pratique qu'un simple nombre la 4 éme, Les paramètres que prennent les instructions.</p> <p>BUILD_MAP créé un dictionnaire puis le mets dans la pile, la pile étant une liste que va utiliser la fonction pour travailler. LOAD_CONST met la constante numéro 1 sur la pile, LOAD_CONST est appelée une deuxième fois pour ajouter à la pile la constante numéro 2 puis STORE_MAP va prendre ce qui a été chargé sur la pile pour en faire un couple clé valeur à ajouter au dictionnaire.</p> <p>Plus précisément</p> <ul class="simple"> <li>BUILD_MAP 2 fait: PILE.append({})</li> <li>LOAD_CONST 1 fait: PILE.append(__code__.co_const[1])</li> <li>LOAD_CONST 2 fait: PILE.append(__code__.co_const[2])</li> <li>STORE_MAP fait: k = PILE.pop(); v = PILE.pop(); PILE[-1][k] = v</li> </ul> <p>Pour les instructions ligne 17 c'est la même chose, jusqu'au STORE_FAST qui stocke le haut de la pile dans la variable 1 (word_to_digit) Le contenue de la variable est mis sur la pile et on utilise BINARY_SUBSCR qui enlève de la pile le dernier et avant-dernier élément pour appliquer l'opérateur [] et mettre le résultat sur la PILE. ça ressemble à ça:</p> <p><pre> i = PILE.pop(); o = PILE.pop(); PILE.append(o[i])</pre> </p> <p>RETURN_VALUE retourne la valeur du haut de la pile.</p> </div> <div class="section" id="ecrire-soi-meme-du-bytecode"> <h2>Ecrire soi même du bytecode</h2> <p>On va réécrire la fonction translate mais contrairement à la version que l'on a vu, on va faire en sorte que le dictionnaire word_to_digit ne soit pas construit à chaque appelle de la fonction, mais directement chargé sur la pile depuis les constantes. Le bytecode va ressembler à ceci:</p> <p><pre> LOAD_CONST 1 0 LOAD_FAST 1 0 BINARY_SUBSCR RETURN_VALUE</pre> </p> <p>co_const va contenir (None, {'one':1, 'two':2}) et co_varnames (number)</p> <p>Remarquez le 0 après LOAD_CONST en fait, certain opérateur on une taille de 2 et d'autre de 0 (unpyc) lorsqu'ils ont une taille de 2, ils doivent être séparés de deux octets de la prochaine instruction d'où le 0.</p> <p>Pour fabriquer un objet code on va utiliser <strong>types.CodeType</strong>. Le constructeur de cette classe est plutôt gourmand en nombre d'argument</p> <p>argcount, kwonlyargcount, nlocals, stacksize, flags, codestring, constants, names, varnames, filename, name, firstlineno, lnotab[, freevars[, cellvars]]</p> <p>Les arguments sont ceux que l'on a vu plus haut, mais sans le préfixe co_ pour ce qui est de filename, et firstlineno, dans la mesure où la fonction est générée, soit vous utilisez un nom de fichier du genre soit vous utilisez __file__ et inspect.currentframe().f_lineno pour le numéro de ligne.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-3">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="kn">import</span> <span class="nn">types</span> <span class="kn">import</span> <span class="nn">inspect</span> <span class="n">bytecode</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">((</span><span class="n">dis</span><span class="o">.</span><span class="n">opmap</span><span class="p">[</span><span class="s1">'LOAD_CONST'</span><span class="p">],</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">dis</span><span class="o">.</span><span class="n">opmap</span><span class="p">[</span><span class="s1">'LOAD_FAST'</span><span class="p">],</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">dis</span><span class="o">.</span><span class="n">opmap</span><span class="p">[</span><span class="s1">'BINARY_SUBSCR'</span><span class="p">],</span> <span class="n">dis</span><span class="o">.</span><span class="n">opmap</span><span class="p">[</span><span class="s1">'RETURN_VALUE'</span><span class="p">]))</span> <span class="n">code</span> <span class="o">=</span> <span class="n">types</span><span class="o">.</span><span class="n">CodeType</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">bytecode</span><span class="p">,</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="p">{</span><span class="s1">'one'</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span> <span class="s1">'two'</span><span class="p">:</span><span class="mi">2</span><span class="p">}),</span> <span class="p">(),</span> <span class="p">(</span><span class="s1">'number'</span><span class="p">,),</span> <span class="vm">__file__</span><span class="p">,</span> <span class="s1">'translate'</span><span class="p">,</span> <span class="n">inspect</span><span class="o">.</span><span class="n">currentframe</span><span class="p">()</span><span class="o">.</span><span class="n">f_lineno</span><span class="p">,</span> <span class="sa">b</span><span class="s1">'</span><span class="se">\x00\x00</span><span class="s1">'</span><span class="p">)</span> </pre> </div> </div> </div> <p>Maintenant que l'on a notre objet code, il faut le mettre dans une fonction, pour ça, on va utiliser, <strong>types.FunctionType</strong>. Le premier argument est l'objet code, le second est un dictionnaire des variables globales.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-4">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="n">translate2</span> <span class="o">=</span> <span class="n">types</span><span class="o">.</span><span class="n">FunctionType</span><span class="p">(</span><span class="n">code</span><span class="p">,</span> <span class="nb">globals</span><span class="p">())</span> <span class="n">translate2</span><span class="p">(</span><span class="s1">'one'</span><span class="p">)</span> <span class="c1"># 1</span> <span class="n">translate2</span><span class="p">(</span><span class="s1">'two'</span><span class="p">)</span> <span class="c1"># 2</span> </pre> </div> </div> </div> <p>Comme on peut le voir, ça marche et comme on peut l'imaginer, c'est plus rapide.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-5">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-5"> <div class="highlight"> <pre class="literal-block"> <span class="nb">print</span><span class="p">(</span><span class="n">timeit</span><span class="o">.</span><span class="n">Timer</span><span class="p">(</span> <span class="s1">'translate(&quot;one&quot;); translate(&quot;two&quot;)'</span><span class="p">,</span> <span class="s2">&quot;from __main__ import translate&quot;</span> <span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">())</span> <span class="c1"># 0.5960358490001454</span> <span class="nb">print</span><span class="p">(</span><span class="n">timeit</span><span class="o">.</span><span class="n">Timer</span><span class="p">(</span> <span class="s1">'translate2(&quot;one&quot;); translate2(&quot;two&quot;)'</span><span class="p">,</span> <span class="s2">&quot;from __main__ import translate2&quot;</span> <span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">())</span> <span class="c1"># 0.30501958599961654</span> </pre> </div> </div> </div> <p>Doit-on écrire du bytecode pour optimiser les programmes ? <img alt="undecided" src="/theme/img/smiley/smiley-undecided.gif" /></p> <p>Non, dans notre exemple, on aurait pu faire ceci:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-6">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-6"> <div class="highlight"> <pre class="literal-block"> <span class="k">def</span> <span class="nf">translate_factory</span><span class="p">():</span> <span class="n">word_to_digit</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;one&quot;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="s2">&quot;two&quot;</span><span class="p">:</span> <span class="mi">2</span><span class="p">}</span> <span class="k">def</span> <span class="nf">translate3</span><span class="p">(</span><span class="n">number</span><span class="p">):</span> <span class="k">return</span> <span class="n">word_to_digit</span><span class="p">[</span><span class="n">number</span><span class="p">]</span> <span class="k">return</span> <span class="n">translate3</span> <span class="n">translate3</span> <span class="o">=</span> <span class="n">translate_factory</span><span class="p">()</span> <span class="nb">print</span><span class="p">(</span><span class="n">timeit</span><span class="o">.</span><span class="n">Timer</span><span class="p">(</span> <span class="s1">'translate3(&quot;one&quot;); translate3(&quot;two&quot;)'</span><span class="p">,</span> <span class="s2">&quot;from __main__ import translate3&quot;</span> <span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">())</span> <span class="c1"># 0.3185168910003995</span> </pre> </div> </div> </div> <p>translate2 est aussi rapide que translate3, il ne faut pas écrire du bytecode, c'est illisible, indébugable, non-maintenable. Par contre le lire peut-être une bonne source d'information pour acquérir des bonne pratique. Un autre exemple d'utilisation du bytecode est <a class="reference external" href="https://ponyorm.com/">l'ORM Pony</a> qui lie le bytecode des instructions python pour les convertires en SQL</p> <p>Dernière chose, Si jamais vous souhaitez utiliser FunctionType pour créer des fonctions dynamiquement en fonction des paramètre utilisateur.</p> <ol class="arabic simple"> <li>C'est une idée complètement Kamikaze au niveau sécurité</li> <li>Ne faites jamais ça.</li> <li>Si vous tenez vraiment à le faire je vous déconseille d'utiliser globals() comme dictionnaire en paramètre de FunctionType préférez, créer votre propre environnement en prenant que de petites choses dans builtins.</li> </ol> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-7">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-7"> <div class="highlight"> <pre class="literal-block"> <span class="kn">import</span> <span class="nn">builtins</span> <span class="n">glob_dict</span> <span class="o">=</span> <span class="p">{</span><span class="n">k</span><span class="p">:</span> <span class="n">v</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="nb">vars</span><span class="p">(</span><span class="n">builtins</span><span class="p">)</span><span class="o">.</span><span class="n">items</span><span class="p">()</span> <span class="k">if</span> <span class="n">k</span> <span class="ow">in</span> <span class="p">(</span><span class="s1">'len'</span><span class="p">,</span> <span class="s1">'list'</span><span class="p">,</span> <span class="s1">'sum'</span><span class="p">,</span> <span class="s1">'str'</span><span class="p">,</span> <span class="s1">'zip'</span><span class="p">)}</span> </pre> </div> </div> </div> <p>Voilou j'espère que vous avez découvert une autre facette du langage python et que cela vous a donné des idées à expérimenter, alors vos IDE et bon code <img alt="laughing" src="/theme/img/smiley/smiley-laughing.gif" /></p> </div> Débuter avec Angular22015-06-10T23:23:00+02:002015-06-10T23:23:00+02:00Vincent Mailloltag:autourducode.com,2015-06-10:/angular2-premier-pas.html<p class="first last">La version alpha d'Angular2 est sortie, nous allons tester ça.</p> <p>Cet article a été écrit alors que Angular2 été en version alpha, beaucoup de choses ont changé depuis la sortie de la version stable j'ai donc fait un autre <a class="reference external" href="http://autourducode.com/angular2-le-tutoriel-interaction-entre-composant.html">tutoriel sur Angular 2</a></p> <p>Salut à vous et bienvenus dans cette initiation à Angular2. <img alt="smile" src="/theme/img/smiley/smiley-smile.gif" /></p> <p>Je dois avouer que <strong>AngularJS2</strong> n'a plus grand chose avoir avec le 1, Plus de scope, plus de ng-app, un nouveau system d'injection de dépendance et un system de détection de changement beaucoup plus performant. Il existe une version Typescript et Dart dans ce tutoriel, les exemples seront en <strong>Typescript</strong>. Pour utiliser Angular2 il va donc falloire installer pas mal de chose dont le compilateur TypeScript.</p> <p>On va donc commencer par installer <strong>node</strong> pour avoir <strong>npm</strong>.</p> <p><kbd class="kbd"> $ wget http://nodejs.org/dist/v0.11.16/node-v0.11.16-linux-x64.tar.gz</kbd> </p> <p><kbd class="kbd"> $ tar -xaf node-v0.11.16-linux-x86.tar.gz</kbd> </p> <p><kbd class="kbd"> $ cd node-v0.11.16-linux-x86/bin/</kbd> </p> <p><kbd class="kbd"> $ export PATH=$PWD:$PATH</kbd> </p> <p>Une fois node installé, on va installer <strong>TypeScript</strong> et <strong>tsd</strong> qui va nous permettre de récupérer des fichier de définition.</p> <p><kbd class="kbd"> $ ./npm install -g typescript&#64;^1.5.0-beta</kbd> </p> <p><kbd class="kbd"> $ ./npm install -g tsd</kbd> </p> <p>L'installation est maintenant terminée, on vous pouvoir commencer à développer. Créer un répertoire qui contiendra votre application. On va ensuite installer dans ce répertoire les fichiers de description d'Angular2.</p> <p><kbd class="kbd"> $ tsd query angular2 --action install</kbd> </p> <p><kbd class="kbd"> $ tsd query es6-promise --action install</kbd> </p> <p><kbd class="kbd"> $ tsd query rx --action install</kbd> </p> <p><kbd class="kbd"> $ tsd query rx-lite --action install</kbd> </p> <p>À la fin de cette étape, un fichier typing a été créé contenant ceci:</p> <p><kbd class="kbd"> $ tree</kbd> </p> <pre class="literal-block"> . +-- typings +-- angular2 | +-- angular2.d.ts +-- es6-promise | +-- es6-promise.d.ts +-- rx +-- rx.d.ts +-- rx-lite.d.ts </pre> <p>On va commencer par écrire notre premier composant en créant un fichier <strong>&quot;app.ts&quot;</strong>. Dans ce fichier, on va importer tout ce dont on a besoin pour ce premier exemple.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">TypeScript </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="kr">import</span> <span class="p">{</span><span class="nx">Component</span><span class="p">,</span> <span class="nx">View</span><span class="p">,</span> <span class="nx">bootstrap</span><span class="p">}</span> <span class="nx">from</span> <span class="s1">'angular2/angular2'</span><span class="p">;</span> </pre> </div> </div> </div> <p>Component qui va nous permettre d'indiquer quelle balise sera utiliser pour insérer notre composant dans une page HTML.View qui indique l'apparence de notre composant. C'est deux fonctions vont décorer une classe dans laquelle se trouvera la logique de notre composant, le contrôleur. Pour cette initiation à Angular2, le contrôleur sera vide, se sera seulement une classe en type script sans rien dedans.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-2">TypeScript </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="kr">class</span> <span class="nx">AppComponent</span> <span class="p">{</span> <span class="p">}</span> </pre> </div> </div> </div> <p>La vue affichera juste un paragraphe avec marqué angular 2. Histoire de pas faire un truc trop bateau, on va quand même faire travailler un peu le moteur de template avec une addition.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-3">TypeScript </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="kd">&#64;View</span><span class="p">({</span> <span class="nx">template</span><span class="o">:</span> <span class="s1">'&lt;p&gt;angular {{ 1 + 1 }}&lt;/p&gt;'</span> <span class="p">})</span> </pre> </div> </div> </div> <p>Dans Angular, vous pouvez faire interpréter du code JS entre une paire de deux accolade. Notre composante pourra s'intégrer à une base html via la balise &lt;my-app&gt;</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-4">TypeScript </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="kd">&#64;Component</span><span class="p">({</span> <span class="nx">selector</span><span class="o">:</span> <span class="s1">'my-app'</span> <span class="p">})</span> </pre> </div> </div> </div> <p>Pour que la composante soie chargée dans la page, la dernière étape est d'appeler la fonction bootstrap</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-5">TypeScript </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-5"> <div class="highlight"> <pre class="literal-block"> <span class="nx">bootstrap</span><span class="p">(</span><span class="nx">MyAppComponent</span><span class="p">);</span> </pre> </div> </div> </div> <p>Voici à quoi doit ressembler votre fichier ts:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-6">TypeScript </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-6"> <div class="highlight"> <pre class="literal-block"> <span class="c1">/// &lt;reference path=&quot;typings/angular2/angular2.d.ts&quot;&gt; </span> <span class="kr">import</span> <span class="p">{</span><span class="nx">Component</span><span class="p">,</span> <span class="nx">View</span><span class="p">,</span> <span class="nx">bootstrap</span><span class="p">}</span> <span class="nx">from</span> <span class="s1">'angular2/angular2'</span><span class="p">;</span> <span class="kd">&#64;Component</span><span class="p">({</span> <span class="nx">selector</span><span class="o">:</span> <span class="s1">'my-app'</span> <span class="p">})</span> <span class="kd">&#64;View</span><span class="p">({</span> <span class="nx">template</span><span class="o">:</span> <span class="s1">' &lt;p&gt;angular {{ 1 + 1 }}&lt;/p&gt; '</span> <span class="p">})</span> <span class="kr">class</span> <span class="nx">AppComponent</span> <span class="p">{</span> <span class="p">}</span> <span class="nx">bootstrap</span><span class="p">(</span><span class="nx">AppComponent</span><span class="p">);</span> </pre> </div> </div> </div> <p>Pour le compiler, lancé la ligne de commande qui suit :</p> <p><kbd class="kbd"> $ tsc -m commonjs -t es5 --emitDecoratorMetadata app.ts</kbd> </p> <p>On va ensuite créer un index.html dans lequel importer notre composant. Entre les balises head, en plus des imports Angularjs2, on va également utiliser Systemjs pour utiliser la fonctionnalité ES6 de chargement de module.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-7">index.html</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-7"> <div class="highlight"> <pre class="literal-block"> <span class="cp">&lt;!DOCTYPE html&gt;</span> <span class="p">&lt;</span><span class="nt">html</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>Angular 2 Quickstart<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&quot;https://github.jspm.io/jmcriffey/bower-traceur-runtime&#64;0.0.87/traceur-runtime.js&quot;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&quot;https://jspm.io/system&#64;0.16.js&quot;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&quot;https://code.angularjs.org/2.0.0-alpha.26/angular2.dev.js&quot;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">my-app</span><span class="p">&gt;&lt;/</span><span class="nt">my-app</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> <span class="nx">System</span><span class="p">.</span><span class="kr">import</span><span class="p">(</span><span class="s1">'app'</span><span class="p">);</span> <span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span> </pre> </div> </div> </div> <p>Reste plus qu'a lancer votre serveur dans un terminal à l'endroit où vous avez créé vos fichiers, lancez:</p> <p><kbd class="kbd"> $ python -m SimpleHTTPServer 8080</kbd> </p> <p>Et rendez-vous dans votre navigateur à l'adresse 127.0.0.1:8080.</p> <div class="section" id="separer-le-template-du-code-typescript"> <h2>Séparer le template du code typescript</h2> <p>S'il y a beaucoup de code html à écrire, le template peut être externalisé dans un autre fichier en utilisant <strong>templateUrl</strong> à la place de <strong>template</strong>. Pour cet exemple, le code du template sera écrit dans le fichier 'app.html'. On va ajouter à notre classe AppComponent un attribut &quot;team_number&quot; de type entier. Pour l'afficher dans le template, on a jute à faire {{ team_number }}</p> </div> <div class="section" id="boucler-sur-une-liste"> <h2>Boucler sur une liste ...</h2> <p>On va ajouter un attribut team à notre classe AppComponent qui contiendra des noms dans un tableau de string. Dans notre template, on va utiliser <strong>la directive NgFor</strong> avant de pouvoir utiliser une directive, il faut l'importer :</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-8">TypeScript </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-8"> <div class="highlight"> <pre class="literal-block"> <span class="kr">import</span> <span class="p">{</span><span class="nx">NgFor</span><span class="p">}</span> <span class="nx">from</span> <span class="s1">'angular2/angular2'</span><span class="p">;</span> </pre> </div> </div> </div> <p>Puis l'ajouter à la liste des directives de notre vue.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-9">TypeScript </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-9"> <div class="highlight"> <pre class="literal-block"> <span class="kd">&#64;View</span><span class="p">({</span> <span class="nx">templateUrl</span><span class="o">:</span> <span class="s1">'app.html'</span><span class="p">,</span> <span class="nx">directives</span><span class="o">:</span> <span class="p">[</span><span class="nx">NgFor</span><span class="p">]</span> <span class="p">})</span> </pre> </div> </div> </div> <p>Une fois cette étape faite, on va pouvoir utiliser la directive ng-for dans le template de la sorte:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-10">html</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-10"> <div class="highlight"> <pre class="literal-block"> <span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">li</span> <span class="err">*</span><span class="na">ng-for</span><span class="o">=</span><span class="s">&quot;#member of team&quot;</span><span class="p">&gt;</span>{{member}}<span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span> </pre> </div> </div> </div> <p>Pour chaque élément dans l'attribut &quot;team&quot; du modèle, la balise li va être répétée avec la création d'une variable &quot;member&quot;. La variable &quot;member&quot; est ensuite utilisée entre les balises grâce à la syntaxe {{member}}.</p> </div> <div class="section" id="utiliser-une-structure-conditionnelle"> <h2>Utiliser une structure conditionnelle ...</h2> <p>La directive est <strong>NgIf</strong>. Comme pour l'utilisation de <strong>NgFor</strong>, il va falloir l'importer et la déclarer dans la liste des directives de la vue.</p> <p>Voici comment on peut l'utiliser dans le template:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-11">html</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-11"> <div class="highlight"> <pre class="literal-block"> <span class="p">&lt;</span><span class="nt">p</span> <span class="err">*</span><span class="na">ng-if</span><span class="o">=</span><span class="s">&quot;team.length == 0&quot;</span><span class="p">&gt;</span>Il n'y a pas de membre dans l'équipe<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span> </pre> </div> </div> </div> <p>Si la longueur de team est de 0 alors la balise est sont contenue sont affichée.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-12">app.ts</a></li> <li><a class="reference internal" href="#code-13">app.html</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-12"> <div class="highlight"> <pre class="literal-block"> <span class="c">/// &lt;reference path=&quot;typings/angular2/angular2.d.ts&quot; /&gt;</span> import <span class="ss">{</span>Component<span class="p">,</span> View<span class="p">,</span> bootstrap<span class="p">,</span> NgFor<span class="p">,</span> NgIf<span class="ss">}</span> from <span class="err">'</span><span class="s">angular2/angular2'; </span><span class="err">&#64;</span>Component<span class="ss">({</span> selector<span class="o">:</span> <span class="err">'</span>my-app<span class="err">'</span> <span class="ss">})</span> <span class="err">&#64;</span>View<span class="ss">({</span> templateUrl<span class="o">:</span> <span class="err">'</span>app<span class="p">.</span>html<span class="err">'</span><span class="p">,</span> directives<span class="o">:</span> <span class="ss">[</span>NgFor<span class="p">,</span> NgIf<span class="ss">]</span> <span class="ss">})</span> class AppComponent <span class="ss">{</span> team<span class="o">:</span> Array<span class="s">&lt;string&gt;</span>; team_number<span class="o">:</span> number; constructor<span class="ss">()</span> <span class="ss">{</span> this<span class="p">.</span>team_number <span class="o">=</span> <span class="mi">1</span>; this<span class="p">.</span>team <span class="o">=</span> <span class="ss">[</span><span class="err">'</span>Daniel Jackson<span class="err">'</span><span class="p">,</span> &quot;Jack O<span class="err">'</span>neal&quot;<span class="ss">]</span>; <span class="ss">}</span> <span class="ss">}</span> bootstrap<span class="ss">(</span>AppComponent<span class="ss">)</span>; </pre> </div> </div> <div class="tab-pane" id="code-13"> <div class="highlight"> <pre class="literal-block"> <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>SG{{team_number}}<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">p</span> <span class="err">*</span><span class="na">ng-if</span><span class="o">=</span><span class="s">&quot;team.length == 0&quot;</span><span class="p">&gt;</span>Il n'y a pas de membre dans l'équipe<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">li</span> <span class="err">*</span><span class="na">ng-for</span><span class="o">=</span><span class="s">&quot;#member of team&quot;</span><span class="p">&gt;</span>{{member}}<span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span> </pre> </div> </div> </div> </div> <div class="section" id="les-entrees-de-l-utilisateur"> <h2>Les entrées de l'utilisateur</h2> <p>On va ajouter une méthode addTeamMember à notre classe AppComponent. Elle prendra en paramètre le nouveau membre de l'équipe. Dans le template, on peut créer une variable qui sera lié dynamiquement à une balise input en ajoutant un nom de la variable précédé d'un # comme attribut à la balise.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-14">TypeScript</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-14"> <div class="highlight"> <pre class="literal-block"> <span class="o">&lt;</span><span class="nx">input</span> <span class="err">#</span><span class="nx">new_member</span> <span class="p">(</span><span class="nx">keyup</span><span class="p">)</span><span class="o">&gt;</span> </pre> </div> </div> </div> <p>On indique entre parenthèses sur quel événement cette variable est modifiée. Pour utiliser la valeur de cette variable dans notre template on utilisera</p> <pre class="literal-block"> {{new_member.value}} </pre> <p>On va ensuite créer un bouton qui appellera la méthode addTeamMemberpour ajouter le nouveau membre à l'équipe. Pour un effet sympa, on va faire en sorte que le membre apparaisse dans l'équipe dés que l'on commence à saisir son nom en créant une balise li à la volée en fonction de si la variable new_member est vide ou pas.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-15">app.ts</a></li> <li><a class="reference internal" href="#code-16">app.html</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-15"> <div class="highlight"> <pre class="literal-block"> <span class="c">/// &lt;reference path=&quot;typings/angular2/angular2.d.ts&quot; /&gt;</span> import <span class="ss">{</span>Component<span class="p">,</span> View<span class="p">,</span> bootstrap<span class="p">,</span> NgFor<span class="p">,</span> NgIf<span class="ss">}</span> from <span class="err">'</span><span class="s">angular2/angular2'; </span><span class="err">&#64;</span>Component<span class="ss">({</span> selector<span class="o">:</span> <span class="err">'</span>my-app<span class="err">'</span> <span class="ss">})</span> <span class="err">&#64;</span>View<span class="ss">({</span> templateUrl<span class="o">:</span> <span class="err">'</span>app<span class="p">.</span>html<span class="err">'</span><span class="p">,</span> directives<span class="o">:</span> <span class="ss">[</span>NgFor<span class="p">,</span> NgIf<span class="ss">]</span> <span class="ss">})</span> class AppComponent <span class="ss">{</span> team<span class="o">:</span> Array<span class="s">&lt;string&gt;</span>; team_number<span class="o">:</span> number; constructor<span class="ss">()</span> <span class="ss">{</span> this<span class="p">.</span>team_number <span class="o">=</span> <span class="mi">1</span>; this<span class="p">.</span>team <span class="o">=</span> <span class="ss">[</span><span class="err">'</span>Daniel Jackson<span class="err">'</span><span class="p">,</span> &quot;Jack O<span class="err">'</span>neal&quot;<span class="ss">]</span>; <span class="ss">}</span> addTeamMember<span class="ss">(</span>member<span class="o">:</span> string<span class="ss">)</span> <span class="ss">{</span> this<span class="p">.</span>team<span class="p">.</span>push<span class="ss">(</span>member<span class="ss">)</span>; <span class="ss">}</span> <span class="ss">}</span> bootstrap<span class="ss">(</span>AppComponent<span class="ss">)</span>; </pre> </div> </div> <div class="tab-pane" id="code-16"> <div class="highlight"> <pre class="literal-block"> <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>SG{{team_number}}<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">input</span> <span class="err">#</span><span class="na">new_member</span> <span class="err">(</span><span class="na">keyup</span><span class="err">)</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">button</span> <span class="err">(</span><span class="na">click</span><span class="err">)=&quot;</span><span class="na">addTeamMember</span><span class="err">(</span><span class="na">new_member</span><span class="err">.</span><span class="na">value</span><span class="err">)&quot;</span><span class="p">&gt;</span>Add Member<span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">p</span> <span class="err">*</span><span class="na">ng-if</span><span class="o">=</span><span class="s">&quot;team.length == 0&quot;</span><span class="p">&gt;</span>Il n'y a pas de membre dans l'équipe<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">li</span> <span class="err">*</span><span class="na">ng-for</span><span class="o">=</span><span class="s">&quot;#member of team&quot;</span><span class="p">&gt;</span>{{member}}<span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">li</span> <span class="err">*</span><span class="na">ng-if</span><span class="o">=</span><span class="s">&quot;new_member.value&quot;</span><span class="p">&gt;</span>{{new_member.value}}<span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span> </pre> </div> </div> </div> <p>C'est pratique, mais, il serait sympa de remettre le champ à vide après avoir ajouté un nouveau membre. Pour faire cela, on va utiliser un objet <strong>Control</strong>. Cet objet se met à jour lorsque la valeur d'un champ est changée et change la valeur de ce champ lorsqu'il est modifié via ça méthode <strong>updateValue</strong>. On va donc créer un nouvel attribut dans notre classe de type Control que l'on appellera &quot;new_member&quot;. Afin d'utiliser control il faut l'importer ainsi que formDirectives. Puis ajouter dans les directives de la vue uniquement <strong>formDirectives</strong>.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-17">TypeScript </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-17"> <div class="highlight"> <pre class="literal-block"> <span class="nx">team</span>: <span class="kt">Array</span><span class="o">&lt;</span><span class="kt">string</span><span class="o">&gt;</span><span class="p">;</span> <span class="nx">team_number</span>: <span class="kt">number</span><span class="p">;</span> <span class="nx">new_member</span>: <span class="kt">Control</span><span class="p">;</span> <span class="kr">constructor</span><span class="p">()</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">team_number</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="k">this</span><span class="p">.</span><span class="nx">team</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'Daniel Jackson'</span><span class="p">,</span> <span class="s2">&quot;Jack O'neal&quot;</span><span class="p">];</span> <span class="k">this</span><span class="p">.</span><span class="nx">new_member</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Control</span><span class="p">(</span><span class="s1">''</span><span class="p">);</span> <span class="p">}</span> </pre> </div> </div> </div> <p>On va modifier la méthode addTeamMember comme ceci:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-18">TypeScript </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-18"> <div class="highlight"> <pre class="literal-block"> <span class="nx">addTeamMember() {</span> <span class="k">this</span><span class="p">.</span><span class="nx">team</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">new_member</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span> <span class="k">this</span><span class="p">.</span><span class="nx">new_member</span><span class="p">.</span><span class="nx">updateValue</span><span class="p">(</span><span class="s1">''</span><span class="p">);</span> <span class="p">}</span> </pre> </div> </div> </div> <p>Dans notre template, on va utiliser la dirrective [ng-form-control] et lui indiquer à quel objet Control on souhaite la liéer. Voici à quoi ressemble notre template et notre fichier ts après les modifications</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-19">app.ts</a></li> <li><a class="reference internal" href="#code-20">app.html</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-19"> <div class="highlight"> <pre class="literal-block"> <span class="c">/// &lt;reference path=&quot;typings/angular2/angular2.d.ts&quot; /&gt;</span> import <span class="ss">{</span>Component<span class="p">,</span> View<span class="p">,</span> bootstrap<span class="p">,</span> NgFor<span class="p">,</span> NgIf<span class="p">,</span> formDirectives<span class="p">,</span> Control<span class="ss">}</span> from <span class="err">'</span><span class="s">angular2/angular2'; </span><span class="err">&#64;</span>Component<span class="ss">({</span> selector<span class="o">:</span> <span class="err">'</span>my-app<span class="err">'</span> <span class="ss">})</span> <span class="err">&#64;</span>View<span class="ss">({</span> templateUrl<span class="o">:</span> <span class="err">'</span>app<span class="p">.</span>html<span class="err">'</span><span class="p">,</span> directives<span class="o">:</span> <span class="ss">[</span>NgFor<span class="p">,</span> NgIf<span class="p">,</span> formDirectives<span class="ss">]</span> <span class="ss">})</span> class AppComponent <span class="ss">{</span> team<span class="o">:</span> Array<span class="s">&lt;string&gt;</span>; team_number<span class="o">:</span> number; new_member<span class="o">:</span> Control; constructor<span class="ss">()</span> <span class="ss">{</span> this<span class="p">.</span>team_number <span class="o">=</span> <span class="mi">1</span>; this<span class="p">.</span>team <span class="o">=</span> <span class="ss">[</span><span class="err">'</span>Daniel Jackson<span class="err">'</span><span class="p">,</span> &quot;Jack O<span class="err">'</span>neal&quot;<span class="ss">]</span>; this<span class="p">.</span>new_member <span class="o">=</span> new Control<span class="ss">(</span><span class="err">''</span><span class="ss">)</span>; <span class="ss">}</span> addTeamMember<span class="ss">()</span> <span class="ss">{</span> this<span class="p">.</span>team<span class="p">.</span>push<span class="ss">(</span>this<span class="p">.</span>new_member<span class="p">.</span>value<span class="ss">)</span>; this<span class="p">.</span>new_member<span class="p">.</span>updateValue<span class="ss">(</span><span class="err">''</span><span class="ss">)</span>; <span class="ss">}</span> <span class="ss">}</span> bootstrap<span class="ss">(</span>AppComponent<span class="ss">)</span>; </pre> </div> </div> <div class="tab-pane" id="code-20"> <div class="highlight"> <pre class="literal-block"> <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>SG{{team_number}}<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">'text'</span> <span class="err">[</span><span class="na">ng-form-control</span><span class="err">]=&quot;</span><span class="na">new_member</span><span class="err">&quot;</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">button</span> <span class="err">(</span><span class="na">click</span><span class="err">)=&quot;</span><span class="na">addTeamMember</span><span class="err">()&quot;</span><span class="p">&gt;</span>Add Member<span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">p</span> <span class="err">*</span><span class="na">ng-if</span><span class="o">=</span><span class="s">&quot;team.length == 0&quot;</span><span class="p">&gt;</span>Il n'y a pas de membre dans l'équipe<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">li</span> <span class="err">*</span><span class="na">ng-for</span><span class="o">=</span><span class="s">&quot;#member of team&quot;</span><span class="p">&gt;</span>{{member}}<span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">li</span> <span class="err">*</span><span class="na">ng-if</span><span class="o">=</span><span class="s">&quot;new_member.value&quot;</span><span class="p">&gt;</span>{{new_member.value}}<span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span> </pre> </div> </div> </div> <p>Voilou j'espère que ça vous a donné envie d'essayer, il y aura d'autre article sur ce sujet peut-être que d'ici là toute aura changé, car il ne faut pas l'oublier, Angular2 n'est pour le moment qu'en version alpha, alors en attendant la béta, à vos IDE et bon code <img alt="laughing" src="/theme/img/smiley/smiley-laughing.gif" /></p> </div> Faire de l'asynchrone avec python2015-04-09T01:07:00+02:002015-04-09T01:07:00+02:00Vincent Mailloltag:autourducode.com,2015-04-09:/python-le-tutoriel-sur-asyncio.html<p class="first last">Asyncio est un module disponible dans python 3.4 qui permet de développer des applications asynchrones avec des entrées-sorties non-bloquantes.</p> <p>asyncio est un module disponible dans python 3.4 qui permet de développer des applications asynchrones avec des entrées-sorties non-bloquantes.</p> <div class="section" id="la-boucle-d-evenement"> <h2>La boucle d'évènement</h2> <p>Toutes applications développées avec asyncio va utiliser une boucle d'évènement qui est un objet de type BaseEventLoop. C'est dans cette boucle que seront placées toute les tâches en cours d'exécution. Pour obtenir une boucle d'évènement, on va utiliser la fonction <strong>get_event_loop()</strong> puis on va appeler la méthode <strong>run_forever()</strong> pour la lancer. Voici un exemple qui ne fait rien d'autre que de lancer la boucle est la referme dès que l'utilisatrice fait un <strong>ctrl-c</strong></p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">Python 3.4 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="kn">import</span> <span class="nn">asyncio</span> <span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">get_event_loop</span><span class="p">()</span> <span class="k">try</span><span class="p">:</span> <span class="n">loop</span><span class="o">.</span><span class="n">run_forever</span><span class="p">()</span> <span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span> <span class="n">loop</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> </pre> </div> </div> </div> <p>C'est palpitant <img alt="wink" src="/theme/img/smiley/smiley-wink.gif" /></p> <p>Ok voyons comment lancer une tâche, <strong>BaseEventLoop</strong> propose deux méthodes pour lancer directement un callable <strong>call_soon</strong> qui lance une callback dés que possible et <strong>call_later</strong> qui la lance au bout d'un certain nombre de secondes. call_later prend en premier paramètre un nombre de secondes et en deuxième n'importe quel objet appelable. Si l'appelable prend des paramètre, il peuvent être passé à la suite à call_later. Voici un exemple qui affiche Hello world au bout de deux secondes</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-2">Python 3.4 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="kn">import</span> <span class="nn">asyncio</span> <span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">get_event_loop</span><span class="p">()</span> <span class="n">loop</span><span class="o">.</span><span class="n">call_later</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="nb">print</span><span class="p">,</span> <span class="s1">'Hello'</span><span class="p">)</span> <span class="n">loop</span><span class="o">.</span><span class="n">call_later</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="nb">print</span><span class="p">,</span> <span class="s1">'World'</span><span class="p">)</span> <span class="k">try</span><span class="p">:</span> <span class="n">loop</span><span class="o">.</span><span class="n">run_forever</span><span class="p">()</span> <span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span> <span class="n">loop</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> </pre> </div> </div> </div> <p>Vous remarquerez que Hello et world sont affiché au même moment, l'appel de call_soon ou call_later ne sont pas bloquant.</p> <p>Lorsque vous utiliserez asyncio, il ne faudra jamais utiliser des fonctions bloquantes, on va tout de suite voir pourquoi. Le code ci-dessous contient une fonction now() dont le seul but est de retourner l'heure joliment formatée puis une fonction qui va bloquer pendant 2 secondes avant de finir de s'exécuter en affichant l'heure à laquelle elle finis.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-3">Python 3.4 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">get_event_loop</span><span class="p">()</span> <span class="k">def</span> <span class="nf">now</span><span class="p">():</span> <span class="k">return</span> <span class="s2">&quot;{:%H:%M:%S}&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">datetime</span><span class="o">.</span><span class="n">today</span><span class="p">())</span> <span class="k">def</span> <span class="nf">syncro_func</span><span class="p">():</span> <span class="n">sleep</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="n">now</span><span class="p">(),</span> <span class="s2">&quot;Done&quot;</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="n">now</span><span class="p">(),</span> <span class="s2">&quot;loop.call_soon&quot;</span><span class="p">)</span> <span class="n">loop</span><span class="o">.</span><span class="n">call_soon</span><span class="p">(</span><span class="n">syncro_func</span><span class="p">)</span> <span class="n">loop</span><span class="o">.</span><span class="n">call_soon</span><span class="p">(</span><span class="n">syncro_func</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="n">now</span><span class="p">(),</span> <span class="s2">&quot;loop.call_soon&quot;</span><span class="p">)</span> <span class="k">try</span><span class="p">:</span> <span class="n">loop</span><span class="o">.</span><span class="n">run_forever</span><span class="p">()</span> <span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span> <span class="n">loop</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> </pre> </div> </div> </div> <p>Voici ce que ce code affiche:</p> <pre class="literal-block"> 23:32:21 loop.call_soon 23:32:21 loop.call_soon 23:32:23 Done 23:32:25 Done </pre> <p>Comme vous pouvez le voir il n'y a pas blocage entre les différents appels de call_soon par contre le premier appel de syncro_func a bloqué la boucle d'événement pendant 2 secondes.</p> </div> <div class="section" id="un-client-http-asynchrone"> <h2>Un client HTTP asynchrone</h2> <p>Avant de faire un client, il va nous falloir un serveur, voici un petit serveur fait avec CherryPy Il propose 3 urls, <a class="reference external" href="http://127.0.0.1:8080/index">http://127.0.0.1:8080/index</a> qui affiche Helo world <a class="reference external" href="http://127.0.0.1:8080/random">http://127.0.0.1:8080/random</a> qui retourne 0, 1 ou 2 <a class="reference external" href="http://127.0.0.1:8080/smile/N">http://127.0.0.1:8080/smile/N</a> où N vaut 0, 1 ou 2 et qui affiche un smile</p> <p><strong>Ce serveur met volontairement 2 secondes à répondre afin que l'on puisse voir ce qui ce passe si l'on envoie plusieurs requêtes.</strong></p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-4">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="kn">import</span> <span class="nn">cherrypy</span> <span class="kn">from</span> <span class="nn">time</span> <span class="k">import</span> <span class="n">sleep</span> <span class="kn">from</span> <span class="nn">random</span> <span class="k">import</span> <span class="n">randint</span> <span class="k">class</span> <span class="nc">HelloWorld</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="nd">&#64;cherrypy</span><span class="o">.</span><span class="n">expose</span> <span class="k">def</span> <span class="nf">index</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">sleep</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="k">return</span> <span class="s2">&quot;Hello World!&quot;</span> <span class="nd">&#64;cherrypy</span><span class="o">.</span><span class="n">expose</span> <span class="k">def</span> <span class="nf">smile</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">n</span><span class="p">):</span> <span class="n">sleep</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="k">return</span> <span class="p">(</span><span class="s1">':-)'</span><span class="p">,</span> <span class="s1">';-)'</span><span class="p">,</span> <span class="s1">':-D'</span><span class="p">)[</span><span class="nb">int</span><span class="p">(</span><span class="n">n</span><span class="p">)]</span> <span class="nd">&#64;cherrypy</span><span class="o">.</span><span class="n">expose</span> <span class="k">def</span> <span class="nf">random</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">sleep</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">))</span> <span class="n">cherrypy</span><span class="o">.</span><span class="n">quickstart</span><span class="p">(</span><span class="n">HelloWorld</span><span class="p">())</span> </pre> </div> </div> </div> <p>Les fonctions non-blocantes que propose asyncio sont en réalité des fonctions qui retournent des <strong>coroutines</strong>. Pour utiliser ces fonctions il faut les appeler dans une autre fonction avec la syntaxe suivante</p> <p><pre> result = yield from fonction_generatrice_de_coroutines</pre> </p> <p>La fonction appelante devra être décoré par <strong>coroutine</strong> elle pourra alors être lancée de façon asynchrone. Dans l'exemple, je vais utiliser <strong>open_connection</strong> qui ouvre une socket TCP. Comme on fait du HTTP, il va faloir parcer un peu la réponse. la réponse HTTP resemble à ça:</p> <pre class="literal-block"> HTTP/1.1 200 OK\r\n Date: Wed, 08 Apr 2015 22:22:27 GMT\r\n Content-Length: 25\r\n Content-Type: text/html;charset=utf-8\r\n \r\n &lt;html&gt;la page html&lt;/html&gt; </pre> <p>Ou Content-Length indique la taille de ma page.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-5">Python 3.4 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-5"> <div class="highlight"> <pre class="literal-block"> <span class="nd">&#64;asyncio</span><span class="o">.</span><span class="n">coroutine</span> <span class="k">def</span> <span class="nf">http_client</span><span class="p">(</span><span class="n">host</span><span class="p">,</span> <span class="n">port</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span> <span class="n">request</span> <span class="o">=</span> <span class="p">(</span><span class="s1">'GET </span><span class="si">{url}</span><span class="s1"> HTTP/1.1</span><span class="se">\r\n</span><span class="s1">'</span> <span class="s1">'Host: </span><span class="si">{host}</span><span class="se">\r\n</span><span class="s1">'</span> <span class="s1">'Connection: Close</span><span class="se">\r\n</span><span class="s1">'</span> <span class="s1">'</span><span class="se">\r\n</span><span class="s1">'</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="o">**</span><span class="nb">locals</span><span class="p">())</span> <span class="n">reader</span><span class="p">,</span> <span class="n">writer</span> <span class="o">=</span> <span class="k">yield from</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">open_connection</span><span class="p">(</span><span class="n">host</span><span class="p">,</span> <span class="n">port</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="s1">'[</span><span class="si">{}</span><span class="s1">] </span><span class="si">{}</span><span class="s1"> connected'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">now</span><span class="p">()))</span> <span class="n">writer</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">encode</span><span class="p">())</span> <span class="n">body_length</span> <span class="o">=</span> <span class="mi">0</span> <span class="n">header_line</span> <span class="o">=</span> <span class="k">yield from</span> <span class="n">reader</span><span class="o">.</span><span class="n">readline</span><span class="p">()</span> <span class="n">header_line</span> <span class="o">=</span> <span class="k">yield from</span> <span class="n">reader</span><span class="o">.</span><span class="n">readline</span><span class="p">()</span> <span class="k">while</span> <span class="n">header_line</span> <span class="o">!=</span> <span class="sa">b</span><span class="s1">'</span><span class="se">\r\n</span><span class="s1">'</span><span class="p">:</span> <span class="n">header</span><span class="p">,</span> <span class="n">value</span> <span class="o">=</span> <span class="n">header_line</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="sa">b</span><span class="s1">':'</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="k">if</span> <span class="n">header</span><span class="o">.</span><span class="n">rstrip</span><span class="p">()</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="o">==</span> <span class="sa">b</span><span class="s1">'content-length'</span><span class="p">:</span> <span class="n">body_length</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="n">header_line</span> <span class="o">=</span> <span class="k">yield from</span> <span class="n">reader</span><span class="o">.</span><span class="n">readline</span><span class="p">()</span> <span class="n">data</span> <span class="o">=</span> <span class="k">yield from</span> <span class="n">reader</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="n">body_length</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="s1">'[</span><span class="si">{}</span><span class="s1">] </span><span class="si">{}</span><span class="s1"> </span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">now</span><span class="p">(),</span> <span class="n">data</span><span class="o">.</span><span class="n">decode</span><span class="p">()))</span> <span class="n">writer</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> <span class="k">return</span> <span class="n">data</span> <span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">get_event_loop</span><span class="p">()</span> <span class="n">asyncio</span><span class="o">.</span><span class="k">async</span><span class="p">(</span><span class="n">http_client</span><span class="p">(</span><span class="s1">'127.0.0.1'</span><span class="p">,</span> <span class="mi">8080</span><span class="p">,</span> <span class="s1">'/index'</span><span class="p">,</span> <span class="s1">'client-1'</span><span class="p">))</span> <span class="n">asyncio</span><span class="o">.</span><span class="k">async</span><span class="p">(</span><span class="n">http_client</span><span class="p">(</span><span class="s1">'127.0.0.1'</span><span class="p">,</span> <span class="mi">8080</span><span class="p">,</span> <span class="s1">'/index'</span><span class="p">,</span> <span class="s1">'client-2'</span><span class="p">))</span> <span class="n">asyncio</span><span class="o">.</span><span class="k">async</span><span class="p">(</span><span class="n">http_client</span><span class="p">(</span><span class="s1">'127.0.0.1'</span><span class="p">,</span> <span class="mi">8080</span><span class="p">,</span> <span class="s1">'/index'</span><span class="p">,</span> <span class="s1">'client-3'</span><span class="p">))</span> <span class="k">try</span><span class="p">:</span> <span class="n">loop</span><span class="o">.</span><span class="n">run_forever</span><span class="p">()</span> <span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span> <span class="n">loop</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> </pre> </div> </div> </div> <p>Voici ce qu'affiche le code:</p> <pre class="literal-block"> [client-2] 00:26:23 connected [client-3] 00:26:23 connected [client-1] 00:26:23 connected [client-2] 00:26:25 Hello World! [client-1] 00:26:25 Hello World! [client-3] 00:26:25 Hello World! </pre> <p>Les requêtes, on bien été traité de manière asynchrone il n'y a pas eu de blocage de la boucle par l'attente d'une réponse.</p> <p>Voici ce que ça donne avec du code bloquant si j'utilise HTTPConnection pour me simplifier la vie :</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-6">Python 3.4 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-6"> <div class="highlight"> <pre class="literal-block"> <span class="nd">&#64;asyncio</span><span class="o">.</span><span class="n">coroutine</span> <span class="k">def</span> <span class="nf">http_client</span><span class="p">(</span><span class="n">host</span><span class="p">,</span> <span class="n">port</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="s1">'[</span><span class="si">{}</span><span class="s1">] </span><span class="si">{}</span><span class="s1"> connected'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">now</span><span class="p">()))</span> <span class="n">connection</span> <span class="o">=</span> <span class="n">HTTPConnection</span><span class="p">(</span><span class="n">host</span><span class="p">,</span> <span class="n">port</span><span class="p">)</span> <span class="n">connection</span><span class="o">.</span><span class="n">request</span><span class="p">(</span><span class="s2">&quot;GET&quot;</span><span class="p">,</span> <span class="n">url</span><span class="p">)</span> <span class="n">response</span> <span class="o">=</span> <span class="n">connection</span><span class="o">.</span><span class="n">getresponse</span><span class="p">()</span> <span class="n">data</span> <span class="o">=</span> <span class="n">response</span><span class="o">.</span><span class="n">read</span><span class="p">()</span> <span class="nb">print</span><span class="p">(</span><span class="s1">'[</span><span class="si">{}</span><span class="s1">] </span><span class="si">{}</span><span class="s1"> </span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">now</span><span class="p">(),</span> <span class="n">data</span><span class="p">))</span> <span class="k">return</span> <span class="n">data</span> <span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">get_event_loop</span><span class="p">()</span> <span class="n">asyncio</span><span class="o">.</span><span class="k">async</span><span class="p">(</span><span class="n">http_client</span><span class="p">(</span><span class="s1">'127.0.0.1'</span><span class="p">,</span> <span class="mi">8080</span><span class="p">,</span> <span class="s1">'/index'</span><span class="p">,</span> <span class="s1">'client-1'</span><span class="p">))</span> <span class="n">asyncio</span><span class="o">.</span><span class="k">async</span><span class="p">(</span><span class="n">http_client</span><span class="p">(</span><span class="s1">'127.0.0.1'</span><span class="p">,</span> <span class="mi">8080</span><span class="p">,</span> <span class="s1">'/index'</span><span class="p">,</span> <span class="s1">'client-2'</span><span class="p">))</span> <span class="n">asyncio</span><span class="o">.</span><span class="k">async</span><span class="p">(</span><span class="n">http_client</span><span class="p">(</span><span class="s1">'127.0.0.1'</span><span class="p">,</span> <span class="mi">8080</span><span class="p">,</span> <span class="s1">'/index'</span><span class="p">,</span> <span class="s1">'client-3'</span><span class="p">))</span> <span class="k">try</span><span class="p">:</span> <span class="n">loop</span><span class="o">.</span><span class="n">run_forever</span><span class="p">()</span> <span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span> <span class="n">loop</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> </pre> </div> </div> </div> <p>Voici un exemple de ce que ce code affiche</p> <pre class="literal-block"> [client-1] 00:32:19 connected [client-1] 00:32:21 b'Hello World!' [client-2] 00:32:21 connected [client-2] 00:32:23 b'Hello World!' [client-3] 00:32:23 connected [client-3] 00:32:25 b'Hello World!' </pre> <p>Vous comprenez maintenant pourquoi c'est mal et pourquoi il faut toujours utiliser des coroutines avec asyncio</p> </div> <div class="section" id="comment-synchroniser-tout-ca"> <h2>Comment synchroniser tout ça</h2> <p>Le but va être de faire une première requête vers l'url random pour avoir un nombre et d'utiliser ce nombre pour généré une url vers smile.</p> <p>La fonction <strong>asyncio.async</strong> renvoie un <strong>objet Future</strong>. Cet objet est similaire au promise en JavaScrip, il possède une méthode <strong>add_done_callback</strong> qui exécute une fonction quand la fonction qui a retourné l'objet Future</p> <p>Dans cet exemple, le code de la fonction http_client reste inchangée. Une fonction give_me_a_smile est ajouté pour générer la nouvelle url.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-7">Python 3.4 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-7"> <div class="highlight"> <pre class="literal-block"> <span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">get_event_loop</span><span class="p">()</span> <span class="k">def</span> <span class="nf">give_me_a_smile</span><span class="p">(</span><span class="n">future</span><span class="p">):</span> <span class="n">num</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">future</span><span class="o">.</span><span class="n">result</span><span class="p">())</span> <span class="n">url</span> <span class="o">=</span> <span class="s1">'/smile?n=</span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">num</span><span class="p">)</span> <span class="n">asyncio</span><span class="o">.</span><span class="k">async</span><span class="p">(</span><span class="n">http_client</span><span class="p">(</span><span class="s1">'127.0.0.1'</span><span class="p">,</span> <span class="mi">8080</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="s1">'client-3'</span><span class="p">))</span> <span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">get_event_loop</span><span class="p">()</span> <span class="n">asyncio</span><span class="o">.</span><span class="k">async</span><span class="p">(</span><span class="n">http_client</span><span class="p">(</span><span class="s1">'127.0.0.1'</span><span class="p">,</span> <span class="mi">8080</span><span class="p">,</span> <span class="s1">'/index'</span><span class="p">,</span> <span class="s1">'client-1'</span><span class="p">))</span> <span class="n">future</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="k">async</span><span class="p">(</span><span class="n">http_client</span><span class="p">(</span><span class="s1">'127.0.0.1'</span><span class="p">,</span> <span class="mi">8080</span><span class="p">,</span> <span class="s1">'/random'</span><span class="p">,</span> <span class="s1">'client-2'</span><span class="p">))</span> <span class="n">future</span><span class="o">.</span><span class="n">add_done_callback</span><span class="p">(</span><span class="n">give_me_a_smile</span><span class="p">)</span> <span class="n">future</span><span class="o">.</span><span class="n">add_done_callback</span><span class="p">(</span><span class="n">give_me_a_smile</span><span class="p">)</span> <span class="k">try</span><span class="p">:</span> <span class="n">loop</span><span class="o">.</span><span class="n">run_forever</span><span class="p">()</span> <span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span> <span class="n">loop</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> </pre> </div> </div> </div> <p>Voici un exemple de ce que ce code peut afficher</p> <pre class="literal-block"> [client-1] 00:52:51 connected [client-2] 00:52:51 connected [client-2] 00:52:53 0 [client-1] 00:52:53 Hello World! [client-3] 00:52:53 connected [client-3] 00:52:53 connected [client-3] 00:52:55 :-) [client-3] 00:52:55 :-) </pre> <p>On vient de faire une synchronisation de type 1 vers 1 ou plusieurs pour faire l'inverse, on va utiliser <strong>asyncio.gather</strong>. Cette fonction prend plusieurs Future pour en retourner une seule qui sera terminée lorsque l'ensemble des futurs passés sont terminés. Le résultat est une liste avec tous les résultats de future passée en paramétré. Dans cet exemple, on fait deux requêtes vers random et quand les requêtes sont fini, on fait la moyenne des résultat pour demander un smile.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-8">Python 3.4 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-8"> <div class="highlight"> <pre class="literal-block"> <span class="kn">from</span> <span class="nn">statistics</span> <span class="k">import</span> <span class="n">mean</span> <span class="k">def</span> <span class="nf">give_me_a_smile</span><span class="p">(</span><span class="n">future</span><span class="p">):</span> <span class="c1"># Conversion str vers entier.</span> <span class="n">num_list</span> <span class="o">=</span> <span class="p">[</span><span class="nb">int</span><span class="p">(</span><span class="n">num</span><span class="p">)</span> <span class="k">for</span> <span class="n">num</span> <span class="ow">in</span> <span class="n">future</span><span class="o">.</span><span class="n">result</span><span class="p">()]</span> <span class="n">num</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">mean</span><span class="p">(</span><span class="n">num_list</span><span class="p">))</span> <span class="n">url</span> <span class="o">=</span> <span class="s1">'/smile?n=</span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">num</span><span class="p">)</span> <span class="n">asyncio</span><span class="o">.</span><span class="k">async</span><span class="p">(</span><span class="n">http_client</span><span class="p">(</span><span class="s1">'127.0.0.1'</span><span class="p">,</span> <span class="mi">8080</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="s1">'client-3'</span><span class="p">))</span> <span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">get_event_loop</span><span class="p">()</span> <span class="n">future_1</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="k">async</span><span class="p">(</span><span class="n">http_client</span><span class="p">(</span><span class="s1">'127.0.0.1'</span><span class="p">,</span> <span class="mi">8080</span><span class="p">,</span> <span class="s1">'/random'</span><span class="p">,</span> <span class="s1">'client-2'</span><span class="p">))</span> <span class="n">future_2</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="k">async</span><span class="p">(</span><span class="n">http_client</span><span class="p">(</span><span class="s1">'127.0.0.1'</span><span class="p">,</span> <span class="mi">8080</span><span class="p">,</span> <span class="s1">'/random'</span><span class="p">,</span> <span class="s1">'client-3'</span><span class="p">))</span> <span class="c1"># Combine les futures 1 et 2</span> <span class="n">future_1_and_2</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">gather</span><span class="p">(</span><span class="n">future_1</span><span class="p">,</span> <span class="n">future_2</span><span class="p">)</span> <span class="n">future_1_and_2</span><span class="o">.</span><span class="n">add_done_callback</span><span class="p">(</span><span class="n">give_me_a_smile</span><span class="p">)</span> </pre> </div> </div> </div> <p>résultat:</p> <pre class="literal-block"> [client-3] 00:58:29 connected [client-2] 00:58:29 connected [client-3] 00:58:31 1 [client-2] 00:58:31 2 [client-3] 00:58:31 connected [client-3] 00:58:33 ;-) </pre> <p><strong>asyncio.gather</strong> peut prendre directement des coroutine, on peut donc écrire directement ceci</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-9">Python 3.4 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-9"> <div class="highlight"> <pre class="literal-block"> <span class="n">coroutine_1</span> <span class="o">=</span> <span class="n">http_client</span><span class="p">(</span><span class="s1">'127.0.0.1'</span><span class="p">,</span> <span class="mi">8080</span><span class="p">,</span> <span class="s1">'/random'</span><span class="p">,</span> <span class="s1">'client-2'</span><span class="p">)</span> <span class="n">coroutine_2</span> <span class="o">=</span> <span class="n">http_client</span><span class="p">(</span><span class="s1">'127.0.0.1'</span><span class="p">,</span> <span class="mi">8080</span><span class="p">,</span> <span class="s1">'/random'</span><span class="p">,</span> <span class="s1">'client-3'</span><span class="p">)</span> <span class="n">future_1_and_2</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">gather</span><span class="p">(</span><span class="n">coroutine_1</span><span class="p">,</span> <span class="n">coroutine_2</span><span class="p">)</span> <span class="n">future_1_and_2</span><span class="o">.</span><span class="n">add_done_callback</span><span class="p">(</span><span class="n">give_me_a_smile</span><span class="p">)</span> </pre> </div> </div> </div> <p>Voilou j'espère que cette mise en bouche d'asyncio vous a plu, d'ici le prochain article, à vos IDE et bon code <img alt="laughing" src="/theme/img/smiley/smiley-laughing.gif" /></p> </div> Les definitions de fonctions, forcer les mots clefs2015-03-30T19:28:30+02:002015-03-30T19:28:30+02:00Vincent Mailloltag:autourducode.com,2015-03-30:/les-definitions-de-fonctions-forcer-les-mots-clefs.html<p class="first last">Comment forcer l'utilisateur à utiliser uniquement des arguments mots clef</p> <p>Les fonctions permettent de faire passer les arguments de plusieurs façons. De manière très classique, les un après les autres.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">Python</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span> <span class="k">print</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span> <span class="n">foo</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span> <span class="c1"># 5 6</span> </pre> </div> </div> </div> <p>On appelle cela des arguments de position.</p> <p>L'autre façon est de nommer les arguments ce qui permet de les passer dans l'ordre que l'on veut.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-2">Python</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="n">foo</span><span class="p">(</span><span class="n">b</span><span class="o">=</span><span class="mi">6</span><span class="p">,</span> <span class="n">a</span><span class="o">=</span><span class="mi">5</span><span class="p">)</span> </pre> </div> </div> </div> <p>On peut également mélanger les deux, mais les arguments de positions doivent être en premier.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-3">Python</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="n">foo</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="mi">6</span><span class="p">)</span> </pre> </div> </div> </div> <p>Mais on peut également forcer l'utilisateur à utiliser uniquement des arguments mots clef comme ceci:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-4">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="o">*</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span> <span class="n">foo</span><span class="p">(</span><span class="n">a</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="mi">6</span><span class="p">)</span> <span class="c1"># OK</span> <span class="n">foo</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span> <span class="c1"># TypeError: foo() takes 0 positional arguments but 2 were given*</span> </pre> </div> </div> </div> <p>En fait, tous les arguments à gauche de l'étoile doivent être passés explicitement.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-5">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-5"> <div class="highlight"> <pre class="literal-block"> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span> <span class="k">pass</span> <span class="n">foo</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="c1"># TypeError: foo() missing 1 required keyword-only argument: 'b'</span> <span class="n">foo</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="mi">6</span><span class="p">)</span> <span class="c1"># OK</span> </pre> </div> </div> </div> <p>&#64; vos IDE et bon code <img alt="wink" src="/theme/img/smiley/smiley-wink.gif" /></p> Le formatage des chaînes de caractères2015-03-18T00:17:00+01:002016-09-19T13:41:00+02:00Vincent Mailloltag:autourducode.com,2015-03-18:/python-formater-les-chaines.html<p class="first last">Cet article va parler de la méthode format de string. Cette méthode est plus puissante que l'opérateur % qui a été déprécié en python 3.2 puis juste déconseillé en python 3.3</p> <p>Cet article va parler de la méthode <strong>format</strong> de string. Cette méthode est plus puissante que l'opérateur <strong>%</strong> qui a été déprécié en python 3.2 puis juste déconseillé en python 3.3</p> <p>Pour simplement remplacer des valeurs :</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="s1">'Salut </span><span class="si">{}</span><span class="s1"> </span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="s1">'Daniel'</span><span class="p">,</span> <span class="s1">'Jackson'</span><span class="p">)</span> <span class="c1"># Salut Daniel Jackson</span> </pre> </div> </div> </div> <p>On peut également indiquer le numéro de paramètre:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-2">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="s1">'Salut </span><span class="si">{1}</span><span class="s1"> </span><span class="si">{0}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="s1">'Daniel'</span><span class="p">,</span> <span class="s1">'Jackson'</span><span class="p">)</span> <span class="c1"># Salut Jackson Daniel</span> <span class="s1">'Salut </span><span class="si">{0}</span><span class="s1"> </span><span class="si">{0}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="s1">'Daniel'</span><span class="p">,</span> <span class="s1">'Jackson'</span><span class="p">)</span> <span class="c1"># Salut Daniel Daniel</span> </pre> </div> </div> </div> <p>Ou utiliser un mapping:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-3">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="s1">'Salut </span><span class="si">{name}</span><span class="s1"> </span><span class="si">{fname}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">fname</span><span class="o">=</span><span class="s1">'Daniel'</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s1">'Jackson'</span><span class="p">)</span> <span class="c1"># 'Salut Jackson Daniel'</span> </pre> </div> </div> </div> <p>Ce qui permet de faire ce genre de chose :</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-4">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="k">def</span> <span class="nf">hello</span><span class="p">(</span><span class="n">fname</span><span class="p">):</span> <span class="n">name</span> <span class="o">=</span> <span class="s1">'Carter'</span> <span class="k">return</span> <span class="s1">'Salut </span><span class="si">{name}</span><span class="s1"> </span><span class="si">{fname}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="o">**</span><span class="nb">locals</span><span class="p">())</span> <span class="n">hello</span><span class="p">(</span><span class="s1">'Samantha'</span><span class="p">)</span> <span class="c1"># 'Salut Carter Samantha'</span> </pre> </div> </div> </div> <div class="section" id="alignement"> <h2>Alignement</h2> <p>Tout se concerne l'alignement est préfixé par : (deux points) après le nom ou l'indice du paramètre à utiliser. On indique en premier si l'on souhaite aligner à gauche, droite ou centré avec respectivement les symboles &lt; &gt; ^ puis la taille du champ</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-5">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-5"> <div class="highlight"> <pre class="literal-block"> <span class="s1">'|</span><span class="si">{:&lt;6}</span><span class="s1">|'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="s1">'Hi'</span><span class="p">)</span> <span class="c1"># '|Hi |'</span> <span class="s1">'|</span><span class="si">{:&gt;6}</span><span class="s1">|'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="s1">'Hi'</span><span class="p">)</span> <span class="c1"># '| Hi|'</span> <span class="s1">'|</span><span class="si">{:^6}</span><span class="s1">|'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="s1">'Hi'</span><span class="p">)</span> <span class="c1"># '| Hi |'</span> </pre> </div> </div> </div> <p>Si l'on souhaite une taille de champs dynamique, rien ne nous empêche de passer la taille du champ en paramètre.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-6">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-6"> <div class="highlight"> <pre class="literal-block"> <span class="n">fruits</span> <span class="o">=</span> <span class="p">(</span><span class="s1">'Kiwi'</span><span class="p">,</span> <span class="s1">'Brugnon'</span><span class="p">,</span> <span class="s1">'Coing'</span><span class="p">)</span> <span class="n">max_length</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">fruits</span><span class="p">)</span> <span class="k">for</span> <span class="n">fruit</span> <span class="ow">in</span> <span class="n">fruits</span><span class="p">:</span> <span class="nb">print</span><span class="p">(</span><span class="s1">'|{fruit:&lt;</span><span class="si">{len}</span><span class="s1">}|'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">fruit</span><span class="o">=</span><span class="n">fruit</span><span class="p">,</span> <span class="nb">len</span><span class="o">=</span><span class="n">max_length</span><span class="p">))</span> <span class="c1"># |Kiwi |</span> <span class="c1"># |Brugnon|</span> <span class="c1"># |Coing |</span> </pre> </div> </div> </div> </div> <div class="section" id="pour-les-objets"> <h2>Pour les objets</h2> <p>Vous pouvez accéder aux attributes d'un objet dans votre chaîne:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-7">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-7"> <div class="highlight"> <pre class="literal-block"> <span class="n">num_comp</span> <span class="o">=</span> <span class="nb">complex</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span> <span class="s1">'real: </span><span class="si">{0.real}</span><span class="s1"> i: </span><span class="si">{0.imag}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">num_comp</span><span class="p">)</span> <span class="c1"># 'real: 2.0 i: 5.0'</span> </pre> </div> </div> </div> <p>mais également au élément d'une liste ou d'un dictionnaire.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-8">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-8"> <div class="highlight"> <pre class="literal-block"> <span class="n">d</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'foo'</span><span class="p">:</span> <span class="s1">'hello'</span><span class="p">}</span> <span class="s1">'</span><span class="si">{0[foo]}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">d</span><span class="p">)</span> <span class="c1"># 'hello'</span> <span class="s1">'</span><span class="si">{0[foo][4]}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">d</span><span class="p">)</span> <span class="c1"># 'l'</span> </pre> </div> </div> </div> <p>Attention cependant, les clefs de votre dictionnaire ne peuvent pas être des chaînes représentant des nombres:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-9">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-9"> <div class="highlight"> <pre class="literal-block"> <span class="n">d</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'55'</span><span class="p">:</span> <span class="s1">'hello'</span><span class="p">}</span> <span class="n">ne</span> <span class="n">marchera</span> <span class="n">pas</span><span class="o">.</span> </pre> </div> </div> </div> </div> <div class="section" id="pour-les-nombres"> <h2>Pour les Nombres</h2> <p>Le nombre de possibilités implémentées est proche de la folie.</p> <p>En plus des operateurs d'alignements &lt; &gt; et ^ il existe aussi =. Il place le nombre à droite et aligne sont signe à gauche.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-10">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-10"> <div class="highlight"> <pre class="literal-block"> <span class="s1">'|</span><span class="si">{:=4}</span><span class="s1">|'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span><span class="p">)</span> <span class="c1"># '|- 2|'</span> </pre> </div> </div> </div> <p>Si vous souhaitez que le signe soie présent pour les nombre positif comme négatif, vous pouvez mettre un + (plus) juste avant la taille du champs:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-11">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-11"> <div class="highlight"> <pre class="literal-block"> <span class="s1">'</span><span class="si">{0:=+4}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="o">-</span><span class="mi">5</span><span class="p">)</span> <span class="c1"># '- 5'</span> <span class="s1">'</span><span class="si">{0:=+4}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="c1"># '+ 5'</span> </pre> </div> </div> </div> <p>Un moins n'affichera que le signe pour les nombres négatif:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-12">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-12"> <div class="highlight"> <pre class="literal-block"> <span class="s1">'</span><span class="si">{0:=-4}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="o">-</span><span class="mi">5</span><span class="p">)</span> <span class="c1"># '- 5'</span> <span class="s1">'</span><span class="si">{0:=-4}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="c1"># ' 5'</span> </pre> </div> </div> </div> <p>Vous pouvez utiliser un espace pour faire en sorte qu'il est toujours un espace avant un nombre positif.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-13">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-13"> <div class="highlight"> <pre class="literal-block"> <span class="s1">'</span><span class="si">{0:=2}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="mi">555</span><span class="p">)</span> <span class="c1"># '555'</span> <span class="s1">'</span><span class="si">{0:= 2}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="o">-</span><span class="mi">555</span><span class="p">)</span> <span class="c1"># '-555'</span> <span class="s1">'</span><span class="si">{0:= 2}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="mi">555</span><span class="p">)</span> <span class="c1"># ' 555'</span> </pre> </div> </div> </div> <p>Mettre des 0 à la place des espaces ?</p> <p>C'est possible, ajoutez un zéro juste avant la taille du champ.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-14">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-14"> <div class="highlight"> <pre class="literal-block"> <span class="s1">'</span><span class="si">{0:=+04}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span><span class="p">)</span> <span class="c1"># '-002'</span> </pre> </div> </div> </div> <p>Avoir en binaire, octal ou hexa ? C'est possible, avec b, o ou x après la taille du champ:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-15">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-15"> <div class="highlight"> <pre class="literal-block"> <span class="s1">'</span><span class="si">{0:=+08b}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="o">-</span><span class="mi">10</span><span class="p">)</span> <span class="c1"># '-0001010'</span> <span class="s1">'</span><span class="si">{0:=+08o}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="o">-</span><span class="mi">10</span><span class="p">)</span> <span class="c1"># '-0000012'</span> <span class="s1">'</span><span class="si">{0:=+08x}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="o">-</span><span class="mi">10</span><span class="p">)</span> <span class="c1"># '-000000a'</span> </pre> </div> </div> </div> <p>J'aimerais un symbole du genre 0x pour indiquer que c'est de l'hexa ? Oui il faut mettre un # après le signe.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-16">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-16"> <div class="highlight"> <pre class="literal-block"> <span class="s1">'</span><span class="si">{0:=+#08x}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="o">-</span><span class="mi">10</span><span class="p">)</span> <span class="c1"># '-0x0000a'</span> <span class="s1">'</span><span class="si">{0:=+#08b}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="o">-</span><span class="mi">10</span><span class="p">)</span> <span class="c1"># '-0b01010'</span> </pre> </div> </div> </div> <p>Et il y a encore plein de possibilités <img alt="laughing" src="/theme/img/smiley/smiley-laughing.gif" /></p> <p>Mais regardons plutôt comment mettre à disposition de l'utilisateur un moyen de formater vos propres objets. Imaginons que vous ayes une classe Pixel, vous pouvez laisser choisir l'utilisateur sur la manière de l'afficher en redéfaisant la méthode <strong>__format__</strong>. Tout ce qui est passé après les ':' sera fournie en paramétre à cette méthode qui devra retourner une string.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-17">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-17"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Pixel</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">r</span> <span class="o">=</span> <span class="mi">255</span> <span class="bp">self</span><span class="o">.</span><span class="n">b</span> <span class="o">=</span> <span class="mi">44</span> <span class="bp">self</span><span class="o">.</span><span class="n">g</span> <span class="o">=</span> <span class="mi">20</span> <span class="k">def</span> <span class="nf">__format__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">format_spec</span><span class="p">):</span> <span class="n">formated</span> <span class="o">=</span> <span class="n">format_spec</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s1">'r'</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">r</span><span class="p">))</span> \ <span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s1">'b'</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">b</span><span class="p">))</span> \ <span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s1">'g'</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">g</span><span class="p">))</span> <span class="k">return</span> <span class="n">formated</span> <span class="n">p</span> <span class="o">=</span> <span class="n">Pixel</span><span class="p">()</span> <span class="nb">print</span><span class="p">(</span><span class="s1">'{0:r,b,g}'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">p</span><span class="p">))</span> <span class="c1"># 255,44,20</span> <span class="nb">print</span><span class="p">(</span><span class="s1">'{0:g.r.b}'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">p</span><span class="p">))</span> <span class="c1"># 20.255.44</span> </pre> </div> </div> </div> </div> <div class="section" id="les-litteraux-formates"> <h2>Les littéraux formatés</h2> <p>La <a class="reference external" href="https://www.python.org/dev/peps/pep-0498/">PEP 498</a>, implèmentée pour la première fois dans la version de <strong>Python 3.6 alpha</strong>, permet de définir une chaîne de caractère qui va chercher les variables à substituer dans l'espace de nom locale.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-18">Python 3.6a </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-18"> <div class="highlight"> <pre class="literal-block"> <span class="n">a</span> <span class="o">=</span> <span class="mi">42</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">60</span> <span class="nb">print</span><span class="p">(</span><span class="n">f</span><span class="s2">&quot;</span><span class="si">{a}</span><span class="s2"> + </span><span class="si">{b}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="c1"># 42 + 60</span> </pre> </div> </div> </div> <p>Mais ce n'est pas tout, on peut même executer des expressions entre les accolades</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-19">Python 3.6a </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-19"> <div class="highlight"> <pre class="literal-block"> <span class="n">a</span> <span class="o">=</span> <span class="mi">42</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">60</span> <span class="nb">print</span><span class="p">(</span><span class="n">f</span><span class="s2">&quot;</span><span class="si">{a}</span><span class="s2"> + </span><span class="si">{b}</span><span class="s2"> = {a + b}&quot;</span><span class="p">)</span> <span class="c1"># 42 + 60 = 102</span> <span class="n">messages</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'toto'</span><span class="p">,</span> <span class="s1">'tata'</span><span class="p">]</span> <span class="nb">print</span><span class="p">(</span><span class="n">f</span><span class="s2">&quot;{messages if messages else '&lt;Pas de messages&gt;' }&quot;</span><span class="p">)</span> <span class="c1"># ['toto', 'tata']</span> <span class="n">messages</span> <span class="o">=</span> <span class="p">[]</span> <span class="nb">print</span><span class="p">(</span><span class="n">f</span><span class="s2">&quot;{messages if messages else '&lt;Pas de messages&gt;' }&quot;</span><span class="p">)</span> <span class="c1"># &lt;Pas de messages&gt;</span> </pre> </div> </div> </div> <p>Comme vous pouvez le voir, on peut vite faire des trucs peu lisibles donc évitez de trop en faire entre les accolades. <img alt="wink" src="/theme/img/smiley/smiley-wink.gif" /></p> <p>J'espère que ça vous a plus alors à vos IDE et bon code. <img alt="laughing" src="/theme/img/smiley/smiley-laughing.gif" /></p> </div> Les annotations2014-11-30T12:20:00+01:002014-11-30T12:20:00+01:00Vincent Mailloltag:autourducode.com,2014-11-30:/les-annotations-de-fonctions.html<p class="first last">Python est un langage à typage dynamique vous pensez alors que faire du type checking est impossible , c'est raté</p> <p>Python est un langage à typage dynamique vous pensez alors que faire du type checking est impossible , c'est raté</p> <p>Les annotations permettent d'indiquer les types de paramètres attendus et retourner par une méthode. Elles ont été introduites en python 3 et sont décrites dans la <a class="reference external" href="https://www.python.org/dev/peps/pep-3107/">PEP 3107</a>. Pour annoter qu'une fonction prend en paramètre un entier et retourne une chaîne on fait comme ceci:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="k">def</span> <span class="nf">fr_to_int</span><span class="p">(</span><span class="n">num</span><span class="p">:</span><span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span> <span class="k">return</span> <span class="p">(</span><span class="s1">'zero'</span><span class="p">,</span> <span class="s1">'un'</span><span class="p">,</span> <span class="s1">'deux'</span><span class="p">)</span><span class="o">.</span><span class="n">index</span><span class="p">(</span><span class="n">num</span><span class="o">.</span><span class="n">lower</span><span class="p">())</span> <span class="nb">print</span><span class="p">(</span><span class="n">fr_to_int</span><span class="p">(</span><span class="s2">&quot;Deux&quot;</span><span class="p">))</span> </pre> </div> </div> </div> <p>Mais ça casse mon duck typing !</p> <p>Pas de panique. <img alt="smile" src="/theme/img/smiley/smiley-smile.gif" /></p> <p>Premièrement si vous passez des arguments avec le mauvais type, il ne se passe rien. De plus, vous pouvez spécifier des expressions dans vos annotations. Dans notre exemple, n'importe quel objet avec une méthode lower ne fera pas cracher le programme on pourrait alors annoter notre fonction comme ceci:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-2">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="k">def</span> <span class="nf">fr_to_int</span><span class="p">(</span><span class="n">num</span><span class="p">:</span> <span class="k">lambda</span> <span class="n">num</span><span class="p">:</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">num</span><span class="p">,</span> <span class="s1">'lower'</span><span class="p">))</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span> <span class="k">return</span> <span class="p">(</span><span class="s1">'zero'</span><span class="p">,</span> <span class="s1">'un'</span><span class="p">,</span> <span class="s1">'deux'</span><span class="p">)</span><span class="o">.</span><span class="n">index</span><span class="p">(</span><span class="n">num</span><span class="o">.</span><span class="n">lower</span><span class="p">())</span> </pre> </div> </div> </div> <p>Ou comme cela:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-3">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="k">def</span> <span class="nf">fr_to_int</span><span class="p">(</span><span class="n">num</span><span class="p">:</span> <span class="s1">'object with lower method'</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span> <span class="k">return</span> <span class="p">(</span><span class="s1">'zero'</span><span class="p">,</span> <span class="s1">'un'</span><span class="p">,</span> <span class="s1">'deux'</span><span class="p">)</span><span class="o">.</span><span class="n">index</span><span class="p">(</span><span class="n">num</span><span class="o">.</span><span class="n">lower</span><span class="p">())</span> </pre> </div> </div> </div> <p>Bref vous pouvez mettre ce que vous souhaitez, mais pour unifier un peu les choses, il y a la <a class="reference external" href="https://www.python.org/dev/peps/pep-0484/">PEP 484</a> qui définie le module <strong>typing</strong>.</p> <p>Les annotations d'une fonction peuvent être récupérées à chaud via l'attribut <strong>__annotations__</strong>.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-4">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="n">fr_to_int</span><span class="o">.</span><span class="vm">__annotations__</span> <span class="c1">#{'return': &lt;class int=&quot;&quot;&gt;, 'num': &lt;function lambda=&quot;&quot;&gt; at 0x7f26b7074950&gt;}</span> </pre> </div> </div> </div> <p>Checker le code pendant son execution peut remonter une erreur au plus tôt et fournir plus d'information que si un mauvais objet fait planté le dernière appele de 20 fonctions empilées. L'inconvénient, c'est que ça ralentit le code. Une solution est de faire de l'analyse statique de code.</p> <div class="section" id="le-module-ast"> <h2>Le module ast</h2> <p>Le <strong>module ast</strong> (&quot;Abstract Syntax Trees&quot;) permet de créer un arbre syntaxique à partir d'une source. L'idée est donc de parcourir l'arbre est de remonter les définitions de fonction, les affectations de variable et les appels de fonctions pour voir si les types sont bien passés. Pour ce faire, on va utiliser la <strong>classe NodeVisitor</strong>. Il suffit d'en hériter et de redéfinir les méthodes préfixées par <strong>visit_</strong> en les suffixant par le type de nœud que l'on veut traiter. La liste des nœuds est détaillée sur <a class="reference external" href="http://greentreesnakes.readthedocs.io/en/latest/nodes.html">greentreesnakes</a>. Par exemple, pour récupérer les définitions de fonction on utilise la <strong>**méthode visit_FunctionDef</strong>. Il est important d'appeler la <strong>méthode generic_visit</strong> à la fin sinon, les nœuds fils ne seront pas parcourus et de ce fait, le code à l'intérieur des fonctions ne serait pas analysé.</p> <p>Voici un exemple d'utilisation:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-5">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-5"> <div class="highlight"> <pre class="literal-block"> <span class="kn">import</span> <span class="nn">ast</span><span class="o">,</span> <span class="nn">sys</span> <span class="k">class</span> <span class="nc">MyVisitor</span><span class="p">(</span><span class="n">ast</span><span class="o">.</span><span class="n">NodeVisitor</span><span class="p">):</span> <span class="k">def</span> <span class="nf">visit_FunctionDef</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">node</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">name</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">generic_visit</span><span class="p">(</span><span class="n">node</span><span class="p">)</span> <span class="n">source</span> <span class="o">=</span> <span class="s1">''' def foo(a0=12): def bar1(): a1 = 33 def bar2(): a1 = 42 '''</span> <span class="n">node</span> <span class="o">=</span> <span class="n">ast</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">source</span><span class="p">)</span> </pre> </div> </div> </div> <p>Avant de vous montrer comment récupérer toutes les infos d'une fonction à partir de son nœud, on va regarder comment récupérer les affectations. Pour ce faire, on utilise la méthode visit_Assign. Le nœud transmit à visite_Assigne contient un attribut value contenant ce qui sera affecté à ma variable et un attribut target contenant une liste des noms de variables qui seront affectés.</p> <p>Pourquoi une liste de variables ? Tout simplement pour les cas d'affectation multiple comme:</p> <p>a = b = 1</p> <p>Deux problèmes se posent avec les affectations :</p> </div> <div class="section" id="comment-stocker-les-variables-et-leur-type"> <h2>1 Comment stocker les variables et leur type ?</h2> <p>Il ne suffit pas de stoker bêtement les variables dans un dico, en effet des variables peuvent avoir des noms identiques, mais se trouver dans des fonctions différentes, de plus les variables définies en dehors d'une fonction doivent être accessible dans la fonction, mais pas l'inverse. Pour résoudre ce problème on va créer une classe Scope qui encapsulera les variables dans des dicts qui seront empilés les un sur les autres au fur et à mesure que l'on descend dans des scopes.</p> <p>Voici la classe Scope</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-6">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-6"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Scope</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_pile</span> <span class="o">=</span> <span class="p">[{}]</span> <span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">var_name</span><span class="p">,</span> <span class="n">var_type</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_pile</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="n">var_name</span><span class="p">]</span> <span class="o">=</span> <span class="n">var_type</span> <span class="k">def</span> <span class="nf">push</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_pile</span><span class="o">.</span><span class="n">append</span><span class="p">({})</span> <span class="k">def</span> <span class="nf">pop</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_pile</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span> <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">var_name</span><span class="p">):</span> <span class="k">for</span> <span class="n">scope</span> <span class="ow">in</span> <span class="nb">reversed</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_pile</span><span class="p">):</span> <span class="k">try</span><span class="p">:</span> <span class="k">return</span> <span class="n">scope</span><span class="p">[</span><span class="n">var_name</span><span class="p">]</span> <span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span> <span class="k">continue</span> <span class="k">raise</span> <span class="ne">NameError</span><span class="p">(</span><span class="s2">&quot;name '</span><span class="si">{}</span><span class="s2">' is not defined&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">var_name</span><span class="p">))</span> </pre> </div> </div> </div> </div> <div class="section" id="comment-recuperer-le-type-de-ma-variable"> <h2>2 Comment récupérer le type de ma variable ?</h2> <p>Pour ça on va parser l'attribut value du nœud passé. Si ce nœud contient une constante numérique 5, 3.69, 4L, Il est du type ast.Num et on récupére la valeur dans n. S'il contient une constante litérale &quot;toto&quot;, il est du type ast.Str et le contenu sera dans l'attribut s. Si c'est une variable, on va essayer de la récupérer dans le scope. Comme je n'ai pas prévu tous les cas, je fais en sorte que le code me laisse un beau message d'erreur en cas de problème.</p> <p>Voici notre visiteur modifié, il empile un nouveau scope avant de visiter les nœuds fils et les dépiles après.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-7">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-7"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">MyVisitor</span><span class="p">(</span><span class="n">ast</span><span class="o">.</span><span class="n">NodeVisitor</span><span class="p">):</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kws</span><span class="p">):</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kws</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">variables</span> <span class="o">=</span> <span class="n">Scope</span><span class="p">()</span> <span class="k">def</span> <span class="nf">_get_type</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">node</span><span class="p">):</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">Num</span><span class="p">):</span> <span class="n">var_type</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">n</span><span class="p">)</span> <span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">Str</span><span class="p">):</span> <span class="n">var_type</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">s</span><span class="p">)</span> <span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">Name</span><span class="p">):</span> <span class="k">try</span><span class="p">:</span> <span class="n">var_type</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">variables</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">id</span><span class="p">)</span> <span class="k">except</span> <span class="ne">KeyError</span> <span class="k">as</span> <span class="n">err</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">NameError</span><span class="p">(</span><span class="s2">&quot;name '</span><span class="si">{}</span><span class="s2">' is not defined&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">value</span><span class="o">.</span><span class="n">id</span><span class="p">))</span> <span class="k">else</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">(</span><span class="s2">&quot;Houps, je l'ai pas vu venir </span><span class="si">{}</span><span class="s2">&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">node</span><span class="p">))</span> <span class="k">return</span> <span class="n">var_type</span> <span class="k">def</span> <span class="nf">visit_FunctionDef</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">node</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">name</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">variables</span><span class="o">.</span><span class="n">push</span><span class="p">()</span> <span class="bp">self</span><span class="o">.</span><span class="n">generic_visit</span><span class="p">(</span><span class="n">node</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">variables</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span> <span class="k">def</span> <span class="nf">visit_Assign</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">node</span><span class="p">):</span> <span class="n">var_type</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_type</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">value</span><span class="p">)</span> <span class="k">for</span> <span class="n">target</span> <span class="ow">in</span> <span class="n">node</span><span class="o">.</span><span class="n">targets</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">variables</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">target</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="n">var_type</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">generic_visit</span><span class="p">(</span><span class="n">node</span><span class="p">)</span> </pre> </div> </div> </div> <p>On a globalement une structure qui tient la route libre à vous de corriger ces défauts pas la suite. La prochaine étape est de récupérer les définitions et les appels de fonction.</p> <p>Le nœud fonction contient un nœud args.args qui contient la liste de tous les arguments de la fonction. On va parcourir cette liste pour stoker la position de l'argument, son nom et son type. Je ne traiterai pas les arguments de type *args ou **kws qui se traitent respectivement avec les attributs <strong>args.varargannotation</strong> et <strong>args.kwargannotation</strong>. Si l'on n'arrive pas à récupérer le type d'un argument, on le mettra à None. Les définitions de fonction seron ajouté au scope comme n'importe quelle variable. Ensuite, on a plus qu'a vérifier que les types soient bien passés lors des appels de fonctions. Le nœud passé en paramètre à la <strong>méthode visit_Call</strong> contient dans args tout les paramètre passé à la fonction.</p> <p>Voici le code final.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-8">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-8"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">MyVisitor</span><span class="p">(</span><span class="n">ast</span><span class="o">.</span><span class="n">NodeVisitor</span><span class="p">):</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kws</span><span class="p">):</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kws</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">variables</span> <span class="o">=</span> <span class="n">Scope</span><span class="p">()</span> <span class="k">def</span> <span class="nf">_get_type</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">node</span><span class="p">):</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">Num</span><span class="p">):</span> <span class="n">var_type</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">n</span><span class="p">)</span> <span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">Str</span><span class="p">):</span> <span class="n">var_type</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">s</span><span class="p">)</span> <span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">Name</span><span class="p">):</span> <span class="k">try</span><span class="p">:</span> <span class="n">var_type</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">variables</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">id</span><span class="p">)</span> <span class="k">except</span> <span class="ne">KeyError</span> <span class="k">as</span> <span class="n">err</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">NameError</span><span class="p">(</span><span class="s2">&quot;name '</span><span class="si">{}</span><span class="s2">' is not defined&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">value</span><span class="o">.</span><span class="n">id</span><span class="p">))</span> <span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">Call</span><span class="p">):</span> <span class="n">var_type</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">variables</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">func</span><span class="o">.</span><span class="n">id</span><span class="p">)[</span><span class="s1">'returns'</span><span class="p">]</span> <span class="k">else</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">(</span><span class="s2">&quot;Houps, je l'ai pas vu venir </span><span class="si">{}</span><span class="s2">&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">node</span><span class="p">))</span> <span class="k">return</span> <span class="n">var_type</span> <span class="k">def</span> <span class="nf">visit_FunctionDef</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">node</span><span class="p">):</span> <span class="n">arguments</span> <span class="o">=</span> <span class="p">{}</span> <span class="k">for</span> <span class="n">pos</span><span class="p">,</span> <span class="n">arg</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">args</span><span class="p">):</span> <span class="k">if</span> <span class="n">arg</span><span class="o">.</span><span class="n">annotation</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span> <span class="n">arguments</span><span class="p">[</span><span class="n">arg</span><span class="o">.</span><span class="n">arg</span><span class="p">]</span> <span class="o">=</span> <span class="n">arguments</span><span class="p">[</span><span class="n">pos</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span> <span class="k">else</span><span class="p">:</span> <span class="n">arguments</span><span class="p">[</span><span class="n">arg</span><span class="o">.</span><span class="n">arg</span><span class="p">]</span> <span class="o">=</span> <span class="n">arguments</span><span class="p">[</span><span class="n">pos</span><span class="p">]</span> <span class="o">=</span> <span class="nb">eval</span><span class="p">(</span><span class="n">arg</span><span class="o">.</span><span class="n">annotation</span><span class="o">.</span><span class="n">id</span><span class="p">)</span> <span class="c1">#node.args.varargannotation</span> <span class="c1">#node.args.kwargannotation</span> <span class="k">if</span> <span class="n">node</span><span class="o">.</span><span class="n">returns</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span> <span class="n">return_type</span> <span class="o">=</span> <span class="kc">None</span> <span class="k">else</span><span class="p">:</span> <span class="n">return_type</span> <span class="o">=</span> <span class="nb">eval</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">returns</span><span class="o">.</span><span class="n">id</span><span class="p">)</span> <span class="n">func_info</span> <span class="o">=</span> <span class="p">{</span> <span class="s1">'args'</span> <span class="p">:</span> <span class="n">arguments</span><span class="p">,</span> <span class="s1">'returns'</span><span class="p">:</span> <span class="n">return_type</span> <span class="p">}</span> <span class="bp">self</span><span class="o">.</span><span class="n">variables</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">func_info</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">variables</span><span class="o">.</span><span class="n">push</span><span class="p">()</span> <span class="bp">self</span><span class="o">.</span><span class="n">generic_visit</span><span class="p">(</span><span class="n">node</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">variables</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span> <span class="k">def</span> <span class="nf">visit_Assign</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">node</span><span class="p">):</span> <span class="n">var_type</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_type</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">value</span><span class="p">)</span> <span class="k">for</span> <span class="n">target</span> <span class="ow">in</span> <span class="n">node</span><span class="o">.</span><span class="n">targets</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">variables</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">target</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="n">var_type</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">generic_visit</span><span class="p">(</span><span class="n">node</span><span class="p">)</span> <span class="k">def</span> <span class="nf">visit_Call</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">node</span><span class="p">):</span> <span class="n">func</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">variables</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">func</span><span class="o">.</span><span class="n">id</span><span class="p">)</span> <span class="k">for</span> <span class="n">pos</span><span class="p">,</span> <span class="n">arg</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">args</span><span class="p">):</span> <span class="n">var_type</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_type</span><span class="p">(</span><span class="n">arg</span><span class="p">)</span> <span class="k">if</span> <span class="n">var_type</span> <span class="o">!=</span> <span class="n">func</span><span class="p">[</span><span class="s1">'args'</span><span class="p">][</span><span class="n">pos</span><span class="p">]:</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Error line </span><span class="si">{line}</span><span class="s2">:&quot;</span> <span class="s2">&quot; </span><span class="si">{func}</span><span class="s2"> argument </span><span class="si">{pos}</span><span class="s2"> must be&quot;</span> <span class="s2">&quot; </span><span class="si">{expected.__name__}</span><span class="s2">, got </span><span class="si">{given}</span><span class="s2">&quot;</span> <span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">line</span><span class="o">=</span><span class="n">node</span><span class="o">.</span><span class="n">lineno</span><span class="p">,</span> <span class="n">func</span><span class="o">=</span><span class="n">node</span><span class="o">.</span><span class="n">func</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="n">pos</span><span class="o">=</span><span class="n">pos</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">given</span><span class="o">=</span><span class="n">var_type</span><span class="p">,</span> <span class="n">expected</span><span class="o">=</span><span class="n">func</span><span class="p">[</span><span class="s1">'args'</span><span class="p">][</span><span class="n">pos</span><span class="p">]))</span> <span class="bp">self</span><span class="o">.</span><span class="n">generic_visit</span><span class="p">(</span><span class="n">node</span><span class="p">)</span> <span class="n">source</span> <span class="o">=</span> <span class="s2">&quot;&quot;&quot; def bar1(a: int, b: str, c:int=42) -&gt; int: pass toto = 78 tata = &quot;salut&quot; tutu = bar1(78, tata, 25) bar1(toto, 45, tutu) &quot;&quot;&quot;</span> <span class="n">node</span> <span class="o">=</span> <span class="n">ast</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">source</span><span class="p">)</span> <span class="n">MyVisitor</span><span class="p">()</span><span class="o">.</span><span class="n">visit</span><span class="p">(</span><span class="n">node</span><span class="p">)</span> </pre> </div> </div> </div> <p>C'est un début et ils restent pas mal de choses à faire qui ne seront pas montré ici par exemple au lancement de python il y a déjà plein de noms définis comme True, False... Il y a également toutes les fonctions standards dont les paramètres d'entrée et de retour ne sont pas connus. Étendre le contrôle des fonctions aux méthodes. Bref pas mal de taf.</p> <p>J'espère que ça vous a plu et que vous n'êtes pas trop frustré de na pas sortir avec une solution clé en main pour faire du typechecking. En attendant le prochain article, A vos IDE et bon code <img alt="laughing" src="/theme/img/smiley/smiley-laughing.gif" /></p> </div> Développer serveur asynchrone2014-11-18T22:32:00+01:002014-11-18T22:32:00+01:00Vincent Mailloltag:autourducode.com,2014-11-18:/developper-un-serveur-asynchrone.html<p class="first last">Un serveur asynchrone est un serveur dont les entrées sont non bloquantes. Cela signifie que lorsque le serveur dialogue avec un client, il n'attend pas qu'un message arrive, si le message est là, il le traite sinon il va voir s'il a reçus le message d'un autre client. Ce type de serveur permet de gérer simultanément un grand nombre de connexions.</p> <p>Un serveur asynchrone est un serveur dont les entrées sont non bloquantes. Cela signifie que lorsque le serveur dialogue avec un client, il n'attend pas qu'un message arrive, si le message est là, il le traite sinon il va voir s'il a reçus le message d'un autre client. Ce type de serveur permet de gérer simultanément un grand nombre de connexions.</p> <div class="section" id="pour-developper-un-serveur-il-nous-faut-des-sockets"> <h2>Pour développer un serveur, il nous faut des sockets</h2> <p><strong>Socket</strong> est une API plutôt bas niveau qui permet de faire dialoguer plusieurs applications entre elles. Les applications peuvent se trouver sur des machines différentes la communication pourra alors se faire via les protocoles TCP ou UDP mais on peut également travailler au niveau de IP. Cette API est disponible en natif dans tout bon langage.</p> </div> <div class="section" id="choisir-son-protocole"> <h2>Choisir son protocole</h2> <p>Il existe plusieurs familles de protocoles, et on va s'intéresser à la famille IPv6. Avec IP, on a le choix entre TCP fiable et UDP non fiable TCP garantie que les paquets arriveront dans l'ordre d'envoie et qu'il n'y aura pas de perte, c'est un protocol connecté.Alors qu'UDP et non connecté il ne garantit rien mais est plus rapide. Un avantage d'UDP sur TCP et qu'il peut faire du multicaste (un seul envoie pour plusieurs destinataires), UDP et TCP utilise tout les deux le protocole IP pour faire leur besogne. Dans cet article, on va partir sur du TCP.</p> <p>Pour créer une socket TCP/IPv6</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">c</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="kt">int</span> <span class="n">ma_socket</span> <span class="o">=</span> <span class="n">socket</span><span class="p">(</span><span class="n">AF_INET6</span><span class="p">,</span> <span class="n">SOCK_STREAM</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> </pre> </div> </div> </div> <p>Pour UPD/IPv6</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-2">c</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="kt">int</span> <span class="n">ma_socket</span> <span class="o">=</span> <span class="n">socket</span><span class="p">(</span><span class="n">AF_INET6</span><span class="p">,</span> <span class="n">SOCK_DGRAM</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> </pre> </div> </div> </div> </div> <div class="section" id="creer-un-serveur-tcp"> <h2>Créer un serveur TCP</h2> <p>Sur un serveur TCP, il y a une socket dédié à l'acceptation des connexions des clients.À chaque connexion acceptée, une socket est créer pour communiquer avec le client. Avant de pouvoir accepter des demandes de connexion, la socket doit être attachée à une adresse et un port. Faire un serveur TCP, demande 4 grandes étapes.</p> <div class="section" id="creation-de-la-socket-du-serveur"> <h3>1 - Création de la socket du serveur</h3> <p>Le serveur doit avoir une socket dédiée à l'acceptation des connexions des clients.À chaque connexion acceptée, une autre socket sera créée pour communiquer avec le client.</p> </div> <div class="section" id="lier-la-socket-du-serveur"> <h3>2 - Lier la socket du serveur</h3> <p>Avant de pouvoir accepter des demandes de connexion, la socket doit être attachée à une adresse et un port. Cela se fait avec la <strong>fonction bind</strong> qui prend en paramètre une socket, et une structure contenant toutes les informations sur l'adresse.</p> </div> <div class="section" id="marquer-la-socket-pour-accepter-les-demandes-de-connexions"> <h3>3 - Marquer la socket pour accepter les demandes de connexions</h3> <p>Cette quatrième étape ce fait avec la <strong>fonction listen</strong>. Qui prend en paramètre la socket et le nombre maximum de sockets en attente d'acceptation cette valeur est limitée à 128. Depuis les noyaux linux 2.2 elle est fixé dans le fichier /proc/sys/net/core/somaxconn</p> </div> <div class="section" id="accepter-les-connexions-des-clients"> <h3>4 - Accepter les connexions des clients</h3> <p>Cette dernière étape ce fait via la <strong>fonction accept</strong>. Elle prend en paramètre la socket puis à deux autres paramètres optionnels qui permettent de récupérer l'adresse de celui qui se connecte. La fonction <strong>accept va créer une nouvelle socket pour chaque connexion</strong>, on peut l'utiliser pour envoyer ou recevoir des messages via les fonctions <strong>send</strong> et <strong>recv</strong>. send prend en paramètre la socket nouvellement créée, une string contenant le message et le nombre d'octets à envoyer. recv quand à elle prend la socket, un tableau de caractère dans lequel copier les octets reçue et la taille de ce tableau.</p> <p>La structure d'un serveur TCP est la suivante</p> <pre class="literal-block"> L.1 Tant que le serveur tourne: L.2 new_connect = accept(...) L.3 requete = recv(new_connect, ...) L.4 send(new_connect, &quot;reponse&quot;) L.5 close(new_connect, ...) </pre> <p>En temps normal, les instructions <strong>accepte et recv sont bloquante</strong>, cela signifie que l'on reste bloqué à la ligne 2 tant que l'on n'a pas de client qui se connecte. Dès qu'un client se connecte, on reste bloquer à la ligne 3 tant qu'il n'envoie rien et aucun autre client peut être traité.</p> <p>Avec des socket non-bloquantes: <strong>accept</strong> retournera -1 s'il n'a pas reçu connexion et la variable <strong>errno</strong> sera mise à <strong>EAGAIN</strong> ou <strong>EWOULDBLOCK</strong>. Même chose pour recv. Pour avoir plusieurs connexions en simultanée, l'idée est la suivante. On créé un tableau de sockets. Lorsque accepte à créer une nouvelle connexion, on la met dans le tableau. On parcourt le tableau pour voir si un client à envoyé une requête, si oui, je lui renvoie une réponse. Après le close, je supprime la socket du tableau. Pour une gestion efficace du tableau de socket l'algo est le suivant. On garde dans une variable 'p' la position du dernier élément de notre tableau. Lors de l'ajout, on ajoute la socket à 'p' et on incrémente 'p', lors de la suppression, la socket à la position 'p' prend la place de la socket supprimer et 'p' est décrémenté de 1.</p> </div> </div> <div class="section" id="un-coup-de-pouce-de-la-libraire-poll"> <h2>Un coup de pouce de la libraire poll</h2> <p>Plutot que de faire un <strong>recv</strong> et de tester la valeur de retour, les system POSIX.1-2001 mettent à disposition <strong>la libraire poll</strong> . Cette libraire contient une <strong>structure pollfd</strong> à laquelle on donne un socket et un événement sur lequel on veut être prévenue. Dans notre cas, on va utiliser l'événement <strong>POLLIN</strong> qui indique qu'il y a des données en attente de lecture. On constitue un tableau de structure puis on appelle la fonction poll sur ce tableau. La <strong>fonction poll</strong> va l'indiquer dans le champ revents de chaque structure si la socket a reçue un message. La fonction poll prend également en paramètre le nombre de milisecondes pendant lequel elle doit attendre qu'une socket reçoive. On peut mettre se paramètre à 0, mais donner un peu de temps peut permettre au CPU de se reposer.</p> </div> <div class="section" id="exemple"> <h2>Exemple</h2> <p>Le client va envoyer un premier mot attendre que le serveur lui envoie &quot;thanx&quot; puis renvoyer un deuxième mot et le serveur va renvoyer les 2 mots qu'il a reçu.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-3">server.c</a></li> <li><a class="reference internal" href="#code-4">client.py</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="cp">#include</span> <span class="cpf">&lt;sys/types.h&gt;</span><span class="cp"> #include</span> <span class="cpf">&lt;sys/socket.h&gt; // socket, bind, listen, accept</span><span class="cp"> </span> <span class="c1">// TCP/IP </span><span class="cp">#include</span> <span class="cpf">&lt;netinet/in.h&gt;</span><span class="cp"> #include</span> <span class="cpf">&lt;netinet/tcp.h&gt;</span><span class="cp"> </span> <span class="c1">// memset </span><span class="cp">#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp"> </span> <span class="c1">// hton </span><span class="cp">#include</span> <span class="cpf">&lt;arpa/inet.h&gt;</span><span class="cp"> </span> <span class="cp">#include</span> <span class="cpf">&lt;unistd.h&gt; // close, sleep</span><span class="cp"> #include</span> <span class="cpf">&lt;fcntl.h&gt;</span><span class="cp"> </span> <span class="cp">#include</span> <span class="cpf">&lt;stdlib.h&gt; // exit</span><span class="cp"> #include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span> <span class="cp">#include</span> <span class="cpf">&lt;errno.h&gt;</span><span class="cp"> </span> <span class="cp">#include</span> <span class="cpf">&lt;poll.h&gt; // Selection d'un fichier sur un evenement.</span><span class="cp"> </span> <span class="cp">#define NB_CLIENT 64 </span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">argv</span><span class="p">[])</span> <span class="p">{</span> <span class="kt">int</span> <span class="n">connection_socket</span><span class="p">;</span> <span class="c1">// Pour une socket non bloquante, on utilise SOCK_STREAM | SOCK_NONBLOCK depuis linux 2.6.27 </span> <span class="k">if</span> <span class="p">((</span><span class="n">connection_socket</span> <span class="o">=</span> <span class="n">socket</span><span class="p">(</span><span class="n">AF_INET6</span><span class="p">,</span> <span class="n">SOCK_STREAM</span> <span class="o">|</span> <span class="n">SOCK_NONBLOCK</span> <span class="p">,</span> <span class="mi">0</span><span class="p">))</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span> <span class="n">perror</span><span class="p">(</span><span class="nb">NULL</span><span class="p">);</span> <span class="p">}</span> <span class="c1">// Creation d'une structure adresse pour lier la socket avec bind </span> <span class="k">struct</span> <span class="n">sockaddr_in6</span> <span class="n">my_addr</span><span class="p">,</span> <span class="n">peer_addr</span><span class="p">;</span> <span class="kt">socklen_t</span> <span class="n">peer_addr_size</span><span class="p">;</span> <span class="n">memset</span><span class="p">(</span><span class="o">&amp;</span><span class="n">my_addr</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="k">struct</span> <span class="n">sockaddr_in6</span><span class="p">));</span> <span class="n">my_addr</span><span class="p">.</span><span class="n">sin6_family</span> <span class="o">=</span> <span class="n">AF_INET6</span><span class="p">;</span> <span class="c1">// Famille IPv6 </span> <span class="n">my_addr</span><span class="p">.</span><span class="n">sin6_addr</span> <span class="o">=</span> <span class="n">in6addr_any</span><span class="p">;</span> <span class="c1">// Adresse IP </span> <span class="n">my_addr</span><span class="p">.</span><span class="n">sin6_port</span> <span class="o">=</span> <span class="n">htons</span><span class="p">(</span><span class="mi">10000</span><span class="p">);</span> <span class="c1">// Port </span> <span class="n">my_addr</span><span class="p">.</span><span class="n">sin6_len</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">my_addr</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">bind</span><span class="p">(</span><span class="n">connection_socket</span><span class="p">,</span> <span class="p">(</span><span class="k">struct</span> <span class="n">sockaddr</span> <span class="o">*</span><span class="p">)</span> <span class="o">&amp;</span><span class="n">my_addr</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">my_addr</span><span class="p">))</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span> <span class="n">perror</span><span class="p">(</span><span class="nb">NULL</span><span class="p">);</span> <span class="p">}</span> <span class="c1">// Nb socket en attente d'acceptation </span> <span class="k">if</span> <span class="p">(</span><span class="n">listen</span><span class="p">(</span><span class="n">connection_socket</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">){</span> <span class="n">perror</span><span class="p">(</span><span class="nb">NULL</span><span class="p">);</span> <span class="p">}</span> <span class="k">struct</span> <span class="n">pollfd</span> <span class="n">peer_sockets</span><span class="p">[</span><span class="n">NB_CLIENT</span><span class="p">];</span> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">NB_CLIENT</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">){</span> <span class="n">peer_sockets</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">events</span> <span class="o">=</span> <span class="n">POLLIN</span> <span class="p">;</span> <span class="n">peer_sockets</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">fd</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span> <span class="p">}</span> <span class="kt">int</span> <span class="n">peer_socket</span><span class="p">;</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">i_last_socket</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="kt">char</span> <span class="n">buffers</span><span class="p">[</span><span class="n">NB_CLIENT</span><span class="p">][</span><span class="mi">64</span><span class="p">];</span> <span class="kt">int</span> <span class="n">nb_char_receiveds</span><span class="p">[</span><span class="n">NB_CLIENT</span><span class="p">];</span> <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">((</span><span class="n">peer_socket</span> <span class="o">=</span> <span class="n">accept</span><span class="p">(</span><span class="n">connection_socket</span><span class="p">,</span> <span class="p">(</span><span class="k">struct</span> <span class="n">sockaddr</span> <span class="o">*</span><span class="p">)</span> <span class="o">&amp;</span><span class="n">peer_addr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">peer_addr_size</span><span class="p">))</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="n">errno</span> <span class="o">!=</span> <span class="n">EAGAIN</span> <span class="o">&amp;&amp;</span> <span class="n">errno</span> <span class="o">!=</span> <span class="n">EWOULDBLOCK</span><span class="p">)</span> <span class="p">{</span> <span class="n">perror</span><span class="p">(</span><span class="nb">NULL</span><span class="p">);</span> <span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="c1">// on rend la socket cliente non-bloquante </span> <span class="c1">// Puis on la stoque à la fin du tableau </span> <span class="n">fcntl</span><span class="p">(</span><span class="n">peer_socket</span><span class="p">,</span> <span class="n">F_SETFL</span><span class="p">,</span> <span class="n">O_NONBLOCK</span><span class="p">);</span> <span class="n">peer_sockets</span><span class="p">[</span><span class="n">i_last_socket</span><span class="o">++</span><span class="p">].</span><span class="n">fd</span> <span class="o">=</span> <span class="n">peer_socket</span><span class="p">;</span> <span class="p">}</span> <span class="kt">int</span> <span class="n">nb_events</span> <span class="o">=</span> <span class="n">poll</span><span class="p">(</span><span class="n">peer_sockets</span><span class="p">,</span> <span class="n">i_last_socket</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">i_last_socket</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="n">peer_sockets</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">revents</span> <span class="o">&amp;</span> <span class="n">POLLIN</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="n">nb_char_receiveds</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="p">{</span> <span class="n">nb_char_receiveds</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+=</span> <span class="n">recv</span><span class="p">(</span><span class="n">peer_sockets</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">fd</span><span class="p">,</span> <span class="n">buffers</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">nb_char_receiveds</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="mi">64</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> <span class="n">send</span><span class="p">(</span><span class="n">peer_sockets</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">fd</span><span class="p">,</span> <span class="n">buffers</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">nb_char_receiveds</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="mi">0</span><span class="p">);</span> <span class="n">close</span><span class="p">(</span><span class="n">peer_sockets</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">fd</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o">&lt;</span> <span class="o">--</span><span class="n">i_last_socket</span><span class="p">)</span> <span class="p">{</span> <span class="n">peer_sockets</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">fd</span> <span class="o">=</span> <span class="n">peer_sockets</span><span class="p">[</span><span class="n">i_last_socket</span><span class="p">].</span><span class="n">fd</span><span class="p">;</span> <span class="n">nb_char_receiveds</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">nb_char_receiveds</span><span class="p">[</span><span class="n">i_last_socket</span><span class="p">];</span> <span class="n">memcpy</span><span class="p">(</span><span class="n">buffers</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">buffers</span><span class="p">[</span><span class="n">i_last_socket</span><span class="p">],</span> <span class="n">nb_char_receiveds</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="n">peer_sockets</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">fd</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span> <span class="n">nb_char_receiveds</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="n">nb_char_receiveds</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">recv</span><span class="p">(</span><span class="n">peer_sockets</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">fd</span><span class="p">,</span> <span class="n">buffers</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="mi">64</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> <span class="n">send</span><span class="p">(</span><span class="n">peer_sockets</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">fd</span><span class="p">,</span> <span class="s">&quot;thanx&quot;</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="n">close</span><span class="p">(</span><span class="n">connection_socket</span><span class="p">);</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> </pre> </div> </div> <div class="tab-pane" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="kn">import</span> <span class="nn">socket</span> <span class="kn">import</span> <span class="nn">errno</span> <span class="kn">from</span> <span class="nn">time</span> <span class="kn">import</span> <span class="n">time</span><span class="p">,</span> <span class="n">sleep</span> <span class="n">HOST</span> <span class="o">=</span> <span class="s2">&quot;localhost&quot;</span> <span class="n">PORT</span> <span class="o">=</span> <span class="mi">10000</span> <span class="n">socket_to_server</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET6</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span> <span class="n">socket_to_server</span><span class="o">.</span><span class="n">connect</span><span class="p">((</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">))</span> <span class="n">socket_to_server</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s2">&quot;Hello&quot;</span><span class="p">)</span> <span class="n">data</span> <span class="o">=</span> <span class="n">socket_to_server</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="c1"># Recevoir &quot;thanx&quot;</span> <span class="k">print</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="n">socket_to_server</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s2">&quot; Word&quot;</span><span class="p">)</span> <span class="n">data</span> <span class="o">=</span> <span class="n">socket_to_server</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">64</span><span class="p">)</span> <span class="k">print</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="n">socket_to_server</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> </pre> </div> </div> </div> <p>Voilà ! Désolé si cet article est peut-être un peu moins fluide que les autres, j'espère que ça vous a plu quand même, n'hésitez pas si vous avez des remarques d'ici là a vos IDE et bon code ! <img alt="laughing" src="/theme/img/smiley/smiley-laughing.gif" /></p> </div> Del est-il utile ?2014-10-22T21:20:00+02:002014-10-22T21:20:00+02:00Vincent Mailloltag:autourducode.com,2014-10-22:/del-est-il-utile.html<p class="first last">La première chose à savoir sur del c'est qu'il sert à supprimer une référence vers un objet et pas l'objet.</p> <p>La première chose à savoir sur <strong>del</strong> c'est qu'il sert à supprimer une référence vers un objet et pas l'objet.</p> <p>En python, à chaque fois que vous créer une variable une entrée est ajouté au dictionnaire globals accecible via la fonction du même nom: <strong>globals</strong>.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="n">a</span> <span class="o">=</span> <span class="mi">5</span> <span class="nb">print</span><span class="p">(</span><span class="nb">globals</span><span class="p">()[</span><span class="s1">'a'</span><span class="p">])</span> <span class="c1"># 5</span> </pre> </div> </div> </div> <p>En utilisant del sur un nom de variable la seule chose que vous faites et d'enlever une entrée de globals</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-2">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="k">del</span> <span class="n">a</span> <span class="nb">globals</span><span class="p">()[</span><span class="s1">'a'</span><span class="p">]</span> <span class="c1"># raise KeyError: 'a'</span> </pre> </div> </div> </div> <p>Si une variable est créé dans le scope d'une fonction, l'utilisation de del la supprimera de ce scope. Pour connaitre la liste des variables locales à une fonction on peut utiliser <strong>locals()</strong>. Par contre il est impossible d'utiliser del pour supprimer une variable du scope parent. Ce serait super crade. <img alt="frown" src="/theme/img/smiley/smiley-frown.gif" /></p> <p>Mais de toute manière, une variable créer dans une fonction n'est pas accessible en dehors de cette fonction. En règle générale, <strong>il n'y a aucune raison qui pousse à l'utilisation de del pour supprimer une variable</strong>. Sauf lors de références cycliques c'est à dire losqu'un objet référence un autre objet qui lui même référence notre premier et que l'on a des problème de performance du au GC.</p> <p>Un autre cas qui me semble intéressant. Supprimer toutes les variables inutiles à la fin d'un module.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-3">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="kn">from</span> <span class="nn">sys</span> <span class="k">import</span> <span class="n">platform</span> <span class="n">is_linux</span> <span class="o">=</span> <span class="s2">&quot;linux&quot;</span> <span class="ow">in</span> <span class="n">platform</span> <span class="k">if</span> <span class="n">is_linux</span><span class="p">:</span> <span class="k">def</span> <span class="nf">foo</span><span class="p">():</span> <span class="c1"># do action for linux</span> <span class="k">pass</span> <span class="k">else</span><span class="p">:</span> <span class="k">def</span> <span class="nf">foo</span><span class="p">():</span> <span class="c1"># do action for other</span> <span class="k">pass</span> </pre> </div> </div> </div> Classes abstraites et duck typing2014-09-16T21:24:00+02:002014-09-16T21:24:00+02:00Vincent Mailloltag:autourducode.com,2014-09-16:/classes-abstraites-et-duck-typing.html<p class="first last">Python c'est du duck typing le type n'importe pas du moment que la méthode demandée existe mais alors, a-t-on besoin de classe abstraite ?</p> <p>Python c'est du duck typing le type n'importe pas du moment que la méthode demandée existe mais alors, a-t-on besoin de classe abstraite ?</p> <div class="section" id="classe-abstraite"> <h2>Classe abstraite</h2> <p>Pour faire large, une classe abstraite est une classe qui à des méthodes abstraites. Elles peuvent représenter des concepts comme par exemple un moyen de transport ou un repas, on a une idée de ce que l'on peut faire avec, mais on ne sait pas comment c'est concrètement.</p> <p>Une façon simple de faire une classe abstraite en python, et de créer une classe et faire retourner une exception <strong>NotImplementedError</strong> aux méthodes que vous souhaitez abstraite.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">Python 3 </a></li> <li><a class="reference internal" href="#code-2">Python 2 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Repas</span><span class="p">:</span> <span class="k">def</span> <span class="nf">inviter</span><span class="p">(</span><span class="n">personne</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">(</span><span class="s2">&quot;You must implement foo's </span><span class="si">%s</span><span class="s2"> method&quot;</span> <span class="o">%</span> <span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="vm">__name__</span><span class="p">)</span> <span class="k">def</span> <span class="nf">manger</span><span class="p">(</span><span class="n">personne</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">(</span><span class="s2">&quot;You must implement foo's </span><span class="si">%s</span><span class="s2"> method&quot;</span> <span class="o">%</span> <span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="vm">__name__</span><span class="p">)</span> </pre> </div> </div> <div class="tab-pane" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Repas</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="k">def</span> <span class="nf">inviter</span><span class="p">(</span><span class="n">personne</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">(</span><span class="s2">&quot;You must implement foo's </span><span class="si">%s</span><span class="s2"> method&quot;</span> <span class="o">%</span> <span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="vm">__name__</span><span class="p">)</span> <span class="k">def</span> <span class="nf">manger</span><span class="p">(</span><span class="n">personne</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">(</span><span class="s2">&quot;You must implement foo's </span><span class="si">%s</span><span class="s2"> method&quot;</span> <span class="o">%</span> <span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="vm">__name__</span><span class="p">)</span> </pre> </div> </div> </div> </div> <div class="section" id="le-module-abc"> <h2>Le module abc</h2> <p>Sinon vous pouvez utiliser le <strong>module abc</strong>. abc est l'abréviation de Abstract Base Class, il a était introduit par la <a class="reference external" href="https://www.python.org/dev/peps/pep-3119/">PEP 3119</a>. Pour l'utiliser, il faut définir <strong>abc.ABCMeta</strong> comme la métaclasse et décorer vos méthodes abstraites avec <strong>abc.abstractmethod</strong>.</p> <p>Voici un exemple.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-3">Python 3 </a></li> <li><a class="reference internal" href="#code-4">Python 2 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="kn">import</span> <span class="nn">abc</span> <span class="k">class</span> <span class="nc">Foo</span><span class="p">(</span><span class="n">metaclass</span><span class="o">=</span><span class="n">abc</span><span class="o">.</span><span class="n">ABCMeta</span><span class="p">):</span> <span class="nd">&#64;abc</span><span class="o">.</span><span class="n">abstractmethod</span> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">pass</span> </pre> </div> </div> <div class="tab-pane" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="kn">import</span> <span class="nn">abc</span> <span class="k">class</span> <span class="nc">Foo</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="vm">__metaclass__</span> <span class="o">=</span> <span class="n">abc</span><span class="o">.</span><span class="n">ABCMeta</span> <span class="nd">&#64;abc.abstractmethod</span> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">pass</span> </pre> </div> </div> </div> <p>Une classe qui a une méthode abstraite (Décoré avec abc.abstractmethod) ne peut pas être instanciée. Par contre,ses classes filles pourront être instanciées si elles ont redéfini les méthodes abstraites.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-5">Python 3 </a></li> <li><a class="reference internal" href="#code-6">Python 2 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-5"> <div class="highlight"> <pre class="literal-block"> <span class="c1"># Foo() # TypeError: Can't instantiate abstract class Foo with abstract methods foo</span> <span class="k">class</span> <span class="nc">Foo2</span><span class="p">(</span><span class="n">Foo</span><span class="p">):</span> <span class="k">pass</span> <span class="c1"># Foo2() # TypeError: Can't instantiate abstract class Foo2 with abstract methods foo</span> <span class="k">class</span> <span class="nc">Foo2</span><span class="p">(</span><span class="n">Foo</span><span class="p">):</span> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="mi">1</span> <span class="n">Foo2</span><span class="p">()</span> <span class="c1"># OK</span> </pre> </div> </div> <div class="tab-pane" id="code-6"> <div class="highlight"> <pre class="literal-block"> <span class="c1"># Foo() # TypeError: Can't instantiate abstract class Foo with abstract methods foo</span> <span class="k">class</span> <span class="nc">Foo2</span><span class="p">(</span><span class="n">Foo</span><span class="p">):</span> <span class="k">pass</span> <span class="c1"># Foo2() # TypeError: Can't instantiate abstract class Foo2 with abstract methods foo</span> <span class="k">class</span> <span class="nc">Foo2</span><span class="p">(</span><span class="n">Foo</span><span class="p">):</span> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="mi">1</span> <span class="n">Foo2</span><span class="p">()</span> <span class="c1"># OK</span> </pre> </div> </div> </div> <p>Le module abc va plus loin, ils vous permet de définir une classe mère abstraite à des classes déjà existantes grâce à la méthode de classe <strong>register</strong>.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-7">Python 3 </a></li> <li><a class="reference internal" href="#code-8">Python 2 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-7"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">FooBar1</span><span class="p">:</span> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="s2">&quot;f1&quot;</span> <span class="k">def</span> <span class="nf">bar</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="s2">&quot;b1&quot;</span> <span class="k">class</span> <span class="nc">FooBar2</span><span class="p">:</span> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="s2">&quot;f2&quot;</span> <span class="k">def</span> <span class="nf">bar</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="s2">&quot;b2&quot;</span> <span class="k">class</span> <span class="nc">AbstractFooBar</span><span class="p">(</span><span class="n">metaclass</span><span class="o">=</span><span class="n">abc</span><span class="o">.</span><span class="n">ABCMeta</span><span class="p">):</span> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">()</span> <span class="k">def</span> <span class="nf">bar</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">()</span> <span class="n">AbstractFooBar</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">FooBar1</span><span class="p">)</span> <span class="n">AbstractFooBar</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">FooBar2</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="nb">isinstance</span><span class="p">(</span><span class="n">FooBar1</span><span class="p">(),</span> <span class="n">AbstractFooBar</span><span class="p">))</span> <span class="c1"># True</span> <span class="nb">print</span><span class="p">(</span><span class="nb">isinstance</span><span class="p">(</span><span class="n">FooBar1</span><span class="p">(),</span> <span class="n">AbstractFooBar</span><span class="p">))</span> <span class="c1"># True</span> </pre> </div> </div> <div class="tab-pane" id="code-8"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">FooBar1</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="s2">&quot;f1&quot;</span> <span class="k">def</span> <span class="nf">bar</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="s2">&quot;b1&quot;</span> <span class="k">class</span> <span class="nc">FooBar2</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="s2">&quot;f2&quot;</span> <span class="k">def</span> <span class="nf">bar</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="s2">&quot;b2&quot;</span> <span class="k">class</span> <span class="nc">AbstractFooBar</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="vm">__metaclass__</span> <span class="o">=</span> <span class="n">abc</span><span class="o">.</span><span class="n">ABCMeta</span> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">()</span> <span class="k">def</span> <span class="nf">bar</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">()</span> <span class="n">AbstractFooBar</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">FooBar1</span><span class="p">)</span> <span class="n">AbstractFooBar</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">FooBar2</span><span class="p">)</span> <span class="k">print</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">FooBar1</span><span class="p">(),</span> <span class="n">AbstractFooBar</span><span class="p">)</span> <span class="c1"># True</span> <span class="k">print</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">FooBar1</span><span class="p">(),</span> <span class="n">AbstractFooBar</span><span class="p">)</span> <span class="c1"># True</span> </pre> </div> </div> </div> <p>Le module collections de python utilise abc, ils post-définis des classes abstraites pour les structures de donnés python.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-9">Python </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-9"> <div class="highlight"> <pre class="literal-block"> <span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">MutableSet</span> <span class="nb">isinstance</span><span class="p">(</span><span class="nb">set</span><span class="p">(),</span> <span class="n">MutableSet</span><span class="p">)</span> <span class="c1"># True</span> </pre> </div> </div> </div> </div> <div class="section" id="duck-typing-ou-classes-abstraites"> <h2>Duck typing ou classes abstraites</h2> <p>Mais revenons à nos canards. Si l'on utilise le duck typing, on n'a pas besoin de classes de base abstraites.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-10">Python 3 </a></li> <li><a class="reference internal" href="#code-11">Python 2 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-10"> <div class="highlight"> <pre class="literal-block"> <span class="k">def</span> <span class="nf">call_foo</span><span class="p">(</span><span class="n">fooable</span><span class="p">):</span> <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">fooable</span><span class="p">,</span> <span class="s2">&quot;foo&quot;</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s2">&quot;first argument of call_foo must have foo method&quot;</span><span class="p">)</span> <span class="k">return</span> <span class="n">fooable</span><span class="o">.</span><span class="n">foo</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="nb">int</span><span class="p">(</span><span class="n">fooable</span><span class="o">.</span><span class="n">foo</span><span class="p">()[</span><span class="mi">1</span><span class="p">:])</span> <span class="k">class</span> <span class="nc">Foo5</span><span class="p">:</span> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="s2">&quot;f5&quot;</span> <span class="nb">print</span><span class="p">(</span><span class="n">call_foo</span><span class="p">(</span><span class="n">FooBar1</span><span class="p">()))</span> <span class="c1"># f</span> <span class="nb">print</span><span class="p">(</span><span class="n">call_foo</span><span class="p">(</span><span class="n">Foo5</span><span class="p">()))</span> <span class="c1"># fffff</span> <span class="nb">print</span><span class="p">(</span><span class="n">call_foo</span><span class="p">(</span><span class="mi">42</span><span class="p">))</span> <span class="c1"># TypeError: first argument of call_foo must have foo method</span> </pre> </div> </div> <div class="tab-pane" id="code-11"> <div class="highlight"> <pre class="literal-block"> <span class="k">def</span> <span class="nf">call_foo</span><span class="p">(</span><span class="n">fooable</span><span class="p">):</span> <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">fooable</span><span class="p">,</span> <span class="s2">&quot;foo&quot;</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s2">&quot;first argument of call_foo must have foo method&quot;</span><span class="p">)</span> <span class="k">return</span> <span class="n">fooable</span><span class="o">.</span><span class="n">foo</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="nb">int</span><span class="p">(</span><span class="n">fooable</span><span class="o">.</span><span class="n">foo</span><span class="p">()[</span><span class="mi">1</span><span class="p">:])</span> <span class="k">class</span> <span class="nc">Foo5</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="s2">&quot;f5&quot;</span> <span class="k">print</span> <span class="n">call_foo</span><span class="p">(</span><span class="n">FooBar1</span><span class="p">())</span> <span class="c1"># f</span> <span class="k">print</span> <span class="n">call_foo</span><span class="p">(</span><span class="n">Foo5</span><span class="p">())</span> <span class="c1"># fffff</span> <span class="k">print</span> <span class="n">call_foo</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span> <span class="c1"># TypeError: first argument of call_foo must have foo method</span> </pre> </div> </div> </div> <dl class="docutils"> <dt>C'est vrai on peut très bien se passer de classe abstraite pour preuve, il n'y a pas de classe abstraite pour les callable ou les objets qui peuvent s'utiliser avec len. Alors pourquoi les utiliser <img alt="undecided" src="/theme/img/smiley/smiley-undecided.gif" /></dt> <dd>? Je vois deux raisons à leur utilisation... La première, centraliser la doc et avoir une interface (ensemble des signatures des méthodes d'un type) explicite. <img alt="smile" src="/theme/img/smiley/smiley-smile.gif" /></dd> </dl> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-12">Python 3 </a></li> <li><a class="reference internal" href="#code-13">Python 2 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-12"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">AbstractFoo</span><span class="p">:</span> <span class="sd">&quot;&quot;&quot;De la doc bien garnie sur Abstract class Foo&quot;&quot;&quot;</span> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot; Cette méthode doit retourner une string formée d'une lettre et d'un nombre. &quot;&quot;&quot;</span> <span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">()</span> <span class="k">def</span> <span class="nf">call_foo</span><span class="p">(</span><span class="n">fooable</span><span class="p">):</span> <span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">fooable</span><span class="p">,</span> <span class="n">AbstractFoo</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s2">&quot;First argument of call_foo must be AbstractFoo instance </span><span class="si">%s</span><span class="s2"> given&quot;</span> <span class="o">%</span> <span class="nb">type</span><span class="p">(</span><span class="n">fooable</span><span class="p">))</span> <span class="k">return</span> <span class="n">fooable</span><span class="o">.</span><span class="n">foo</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="nb">int</span><span class="p">(</span><span class="n">fooable</span><span class="o">.</span><span class="n">foo</span><span class="p">()[</span><span class="mi">1</span><span class="p">:])</span> <span class="n">call_foo</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c1"># TypeError: First argument of call_foo must be AbstractFoo instance &lt;class int=&quot;&quot;&gt; given&lt;/class&gt;</span> </pre> </div> </div> <div class="tab-pane" id="code-13"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">AbstractFoo</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot;De la doc bien garnie sur Abstract class Foo&quot;&quot;&quot;</span> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot; Cette méthode doit retourner une string formée d'une lettre et d'un nombre. &quot;&quot;&quot;</span> <span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">()</span> <span class="k">def</span> <span class="nf">call_foo</span><span class="p">(</span><span class="n">fooable</span><span class="p">):</span> <span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">fooable</span><span class="p">,</span> <span class="n">AbstractFoo</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s2">&quot;First argument of call_foo must be AbstractFoo instance </span><span class="si">%s</span><span class="s2"> given&quot;</span> <span class="o">%</span> <span class="nb">type</span><span class="p">(</span><span class="n">fooable</span><span class="p">))</span> <span class="k">return</span> <span class="n">fooable</span><span class="o">.</span><span class="n">foo</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="nb">int</span><span class="p">(</span><span class="n">fooable</span><span class="o">.</span><span class="n">foo</span><span class="p">()[</span><span class="mi">1</span><span class="p">:])</span> <span class="n">call_foo</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c1"># TypeError: First argument of call_foo must be AbstractFoo instance &lt;class int=&quot;&quot;&gt; given&lt;/class&gt;</span> </pre> </div> </div> </div> <p>En cas d'erreur, vous irez voir AbstractFoo et par la même occasion la méthode foo.Ce sera alors clair pour vous de ce que doit faire la méthode foo. Je me vois mal expliquer cela dans call_foo surtout si je fait une librairie avec des dizaines de fonctions prenant des fooable.</p> <p>La deuxième raison est que si un objet a beaucoup de méthodes à redéfinir, c'est plus facile de faire un seul <strong>isinstance</strong> à l'entrer d'une fonction que de un <strong>hasattr</strong> par méthode.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-14">Python 3 </a></li> <li><a class="reference internal" href="#code-15">Python 2 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-14"> <div class="highlight"> <pre class="literal-block"> <span class="k">def</span> <span class="nf">call_foobar</span><span class="p">(</span><span class="n">foobar</span><span class="p">):</span> <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">foobar</span><span class="p">,</span> <span class="s1">'foo'</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s2">&quot;where is foo method ?&quot;</span><span class="p">)</span> <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">foobar</span><span class="p">,</span> <span class="s1">'bar'</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s2">&quot;where is barton killer ?&quot;</span><span class="p">)</span> </pre> </div> </div> <div class="tab-pane" id="code-15"> <div class="highlight"> <pre class="literal-block"> <span class="k">def</span> <span class="nf">call_foobar</span><span class="p">(</span><span class="n">foobar</span><span class="p">):</span> <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">foobar</span><span class="p">,</span> <span class="s1">'foo'</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s2">&quot;where is foo method ?&quot;</span><span class="p">)</span> <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">foobar</span><span class="p">,</span> <span class="s1">'bar'</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s2">&quot;where is barton killer ?&quot;</span><span class="p">)</span> </pre> </div> </div> </div> <p>Bref, faire des classes abstraites en python permettent de cadrer un peu les choses car si le duck typing apporte de la souplesse, il peut aussi apporter de la confusion et il faut trouver le juste milieu. Voilou, j'espère que ça vous a plus alors à vos IDE et bon code. <img alt="smile" src="/theme/img/smiley/smiley-smile.gif" /></p> </div> M4 Tout n'est que substitution2014-09-11T23:44:00+02:002014-09-11T23:44:00+02:00Vincent Mailloltag:autourducode.com,2014-09-11:/m4.html<p class="first last">Dans cet article, on va faire un peu d'archéologie, et étudier M4 sortie en 1977, c'est le grand successeur de M3.</p> <p>Dans cet article, on va faire un peu d'archéologie, et étudier <strong>M4</strong> sortie en 1977, c'est le grand successeur de <strong>M3</strong>.</p> <p>M4 est un processeur de macros écrit dans les laboratoires de Bell par Brian et Dennis, leur nom de famille étant respectivement Ritchie et Kernighan, je les donne au cas où il y est d'autre acolyte aussi populaire avec les même prénoms <img alt="smile" src="/theme/img/smiley/smiley-smile.gif" /> . En M4, le principe de base est la substitution, vous ne pouvez rien faire d'autre. La premiére chose à faire est de définir une macro avec define ainsi si dans un fichier text, hello.m4, je met ceci:</p> <pre class="literal-block"> define(foo, bar) foo </pre> <p>Et que j'appele</p> <p><kbd class="kbd"> $ m4 hello.m4</kbd> </p> <p>Je vais avoir bar qui s'affiche. Facile n'est-ce pas ?</p> <div class="section" id="l-importance-des-quotes"> <h2>L'importance des quotes</h2> <p>M4 cherche par tout les moyens de substitué sauf si vous entouré du text par ceci <strong>`'</strong>, M4 supprimera les quotes mais ne substitura pas l'intérieure.</p> <pre class="literal-block"> define(`foo', one) # defini une macro foo -&gt; one define(`foo', two) # redefini la macro foo -&gt; two define(foo, one) # defini une macro foo -&gt; one define(foo, two) # defini une macro one -&gt; two </pre> </div> <div class="section" id="les-parametres"> <h2>Les paramètres</h2> <p>Une macro peut avoir des paramètre les noms de ses paramètre sont prédéfini. Ainsi, $1 est le 1er argument, $2 le second, ect... On les utilises dirrectement dans la substitution.</p> <pre class="literal-block"> define(`hello', `Salut $1 !') hello(`toto') # Salut toto ! </pre> <p>Il existe d'autres paramètres prédéfinis <strong>$0</strong> est le nom de la macro. <strong>$#</strong> le nombre d'arguments passé à la macro. <strong>$&#64;</strong> la concaténation de tous les arguments passée séparée par <strong>','</strong> (une virgule)</p> </div> <div class="section" id="les-substitutions-conditionnelles-avec-ifelse"> <h2>Les substitutions conditionnelles avec ifelse</h2> <p>ifelse est une macro très puissante. Si le 1er argument est égal au second, elle affiche le 3éme. Si vous donné 4 arguments elle affiche le 4éme si le 1er est différent du 2éme. Avec plus de 4 arguments vous faites un swith case.</p> <pre class="literal-block"> ifelse(`a', `a', `33') # 33 ifelse(`a', `b', `33') # ifelse(`a', `b', `33', `44') # 44 ifelse(`a', `b', `33', `a', `a', `55', `77') # 55 ifelse(`a', `b', `33', `a', `c', `55', `77') # 77 </pre> </div> <div class="section" id="il-ne-nous-manque-plus-que-shift"> <h2>Il ne nous manque plus que shift</h2> <p>Shift prend N paramètre est les affiches tous sauf le premier. Avec les macro précédentes on peut faire tout ce que l'on veux avec un peu de récursivités. Voici une macro qui indique si le premier argument est dans les autres</p> <pre class="literal-block"> define(`in', `ifelse(`$#', 0, `false', # Si pas d'argument on affiche false `$#', 1, `false', # Sinon si un seul d'argument on affiche false `$1', `$2', `true', # Sinon si l'argument 1 est égale au 2éme on affiche true `$#', 2, `false', # Sinon s'il n'y avait que 2 arguments on affiche false `in(`$1', shift(shift($&#64;)))')') # sinon on substitue avec la macro in # en retirant le 2er argument in(1, 2, 3, 1) # -&gt; true in(1, 2, 3, 4) # -&gt; false </pre> </div> <div class="section" id="inverser-les-parametres"> <h2>Inverser les paramétres</h2> <pre class="literal-block"> define(`reverse', `ifelse(`$#', `0', , `$#', `1', ``$1'', `reverse(shift($&#64;)), `$1'')') reverse(3,4,5) # 5,4,3 </pre> <p>Vous trouverez plein <a class="reference external" href="http://193.87.95.148/openwrt/raspip/trunk/build_dir/host/m4-1.4.17/examples/">d'autre macro ici</a> comme forloop</p> <pre class="literal-block"> forloop(`i', `1', `8', `i ') # 12345678 </pre> </div> <div class="section" id="ok-mais-on-a-fait-quoi-avec-m4"> <h2>Ok, mais on a fait quoi avec M4 ?</h2> <p>C'est utiliser par autoconf et sendmail, ou pour factoriser du code assembleur. Certaines personnes ont utilisé M4 pour générer leur html. Il y des libs <a class="reference external" href="http://jon.es/htm4l/htm4l/">htm4l</a> Pour moi, ça ressemble juste à une grosse blague sauf qu'il y a des sites maintenus avec <a class="reference external" href="https://www.finder.com.au/">finder</a>.</p> <p>Mais par contre, le M4 dans le CSS, ça roxe du poney !</p> <pre class="literal-block"> define(`COLOR1', `#FF00') #element1 { background-color: COLOR1; } .element2 { color: COLOR1; } </pre> <p>Oups! j'ai oublié de vous mettre en garde... Voici avertissement que l'on peut trouver en introduction du manuel M4</p> <blockquote> Certaines personnes trouvent m4 assez addictif. Ils utilisent d'abord m4 pour des problèmes simples, puis relever des défis toujours plus grands, apprennent à écrire des ensembles complexes de macros m4. Une fois vraiment accro, les utilisateurs continue avec l'écriture d'applications m4 sophistiquées même pour résoudre des problèmes simples, en consacrant plus de temps au débogage de leurs scripts m4 que de faire véritablement le travail. Méfiez-vous, m4 peut être dangereux pour la santé des programmeurs compulsifs.</blockquote> <p>Mais, j'imagine que vous n'été pas du genre à essayer de programmer un automate cellulaire en M4 pendant plus de 72 heures. X)</p> <p>Si vous souhaitez aller plus loin:</p> <ul class="simple"> <li><a class="reference external" href="http://wolfram.schneider.org/bsd/7thEdManVol2/m4/m4.pdf">The M4 Macro Processor</a> (Article de 1977 écrit par Kernighan et Ritchie)</li> <li><a class="reference external" href="http://www.gnu.org/software/m4/manual/m4-1.4.15/html_node/Intro.html">M4 manual</a> (Le manuel M4)</li> </ul> <p>D'ici là, à vos PDP-11 et bon code ! <img alt="laughing" src="/theme/img/smiley/smiley-laughing.gif" /></p> </div> Le design pattern observer et ses variantes2014-09-08T22:46:00+02:002014-09-08T22:46:00+02:00Vincent Mailloltag:autourducode.com,2014-09-08:/le-design-pattern-observer-et-ses-variantes.html<p class="first last">L'observer est un design patter dans lequel un objet l'observer va s'abonner à un autre objet, le sujet, et attendre que celui-ci le notifie d'un événement. Rien à voir avec le voyeurisme.</p> <p>L'observer est un design pattern dans lequel un objet <strong>l'observer</strong> va s'abonner à un autre objet, <strong>le sujet</strong>, et attendre que celui-ci le notifie d'un événement. Rien à voir avec le voyeurisme.</p> <p>Un événement est généralement un changement d'états de l'objet observé. L'objet observé est appelé le sujet ou l'observable. Un observable peut rattacher plusieurs observer à lui-même.</p> <p>Voici la classe observable, elle possède une méthode add_observer pour lier un observer et une méthode notify_observer qui lorsqu'elle est appelée exécute la méthode notify de tout les observers liés.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">Python 3 </a></li> <li><a class="reference internal" href="#code-2">Python 2 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Observable</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">observers</span> <span class="o">=</span> <span class="p">[]</span> <span class="k">def</span> <span class="nf">notify_observers</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">for</span> <span class="n">obs</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">observers</span><span class="p">:</span> <span class="n">obs</span><span class="o">.</span><span class="n">notify</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="k">def</span> <span class="nf">add_observer</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obs</span><span class="p">):</span> <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">obs</span><span class="p">,</span> <span class="s1">'notify'</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;First argument must be object with notify method&quot;</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">observers</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">obs</span><span class="p">)</span> </pre> </div> </div> <div class="tab-pane" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Observable</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">observers</span> <span class="o">=</span> <span class="p">[]</span> <span class="k">def</span> <span class="nf">notify_observers</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">for</span> <span class="n">obs</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">observers</span><span class="p">:</span> <span class="n">obs</span><span class="o">.</span><span class="n">notify</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="k">def</span> <span class="nf">add_observer</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obs</span><span class="p">):</span> <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">obs</span><span class="p">,</span> <span class="s1">'notify'</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;First argument must be object with notify method&quot;</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">observers</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">obs</span><span class="p">)</span> </pre> </div> </div> </div> <p>Pour vous montrer un exemple d'utilisation, on va créer deux observers, Speed qui affiche la vitesse d'une voiture et Rpm qui affiche les rotations par minute du moteur.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-3">Python 3 </a></li> <li><a class="reference internal" href="#code-4">Python 2 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Speed</span><span class="p">:</span> <span class="k">def</span> <span class="nf">notify</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">subject</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;speed&quot;</span><span class="p">,</span> <span class="n">subject</span><span class="o">.</span><span class="n">speed</span><span class="p">)</span> <span class="k">class</span> <span class="nc">Rpm</span><span class="p">:</span> <span class="k">def</span> <span class="nf">notify</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">subject</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;rpm&quot;</span><span class="p">,</span> <span class="n">subject</span><span class="o">.</span><span class="n">rpm</span><span class="p">)</span> </pre> </div> </div> <div class="tab-pane" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Speed</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="k">def</span> <span class="nf">notify</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">subject</span><span class="p">):</span> <span class="k">print</span> <span class="s2">&quot;speed&quot;</span><span class="p">,</span> <span class="n">subject</span><span class="o">.</span><span class="n">speed</span> <span class="k">class</span> <span class="nc">Rpm</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="k">def</span> <span class="nf">notify</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">subject</span><span class="p">):</span> <span class="k">print</span> <span class="s2">&quot;rpm&quot;</span><span class="p">,</span> <span class="n">subject</span><span class="o">.</span><span class="n">rpm</span> </pre> </div> </div> </div> <p>La classe voiture va hériter de Observable et va appeler la méthode notify_observers à chaque changement de rpm via la méthode accelerate ou de vitesse via les méthode downshift et upshift.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-5">Python 3 </a></li> <li><a class="reference internal" href="#code-6">Python 2 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-5"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Car</span><span class="p">(</span><span class="n">Observable</span><span class="p">):</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">Observable</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">speed</span> <span class="o">=</span> <span class="mi">0</span> <span class="bp">self</span><span class="o">.</span><span class="n">rpm</span> <span class="o">=</span> <span class="mi">0</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox</span> <span class="o">=</span> <span class="p">[</span><span class="o">.</span><span class="mi">016</span><span class="p">,</span> <span class="mf">0.038</span><span class="p">,</span> <span class="o">.</span><span class="mi">038</span><span class="p">,</span> <span class="o">.</span><span class="mi">042</span><span class="p">]</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">def</span> <span class="nf">accelerate</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">rpm</span> <span class="o">+=</span> <span class="mi">1000</span> <span class="bp">self</span><span class="o">.</span><span class="n">speed</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">rpm</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span><span class="p">]</span> <span class="bp">self</span><span class="o">.</span><span class="n">notify_observers</span><span class="p">()</span> <span class="k">def</span> <span class="nf">downshift</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span> <span class="o">-=</span> <span class="mi">1</span> <span class="bp">self</span><span class="o">.</span><span class="n">speed</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">rpm</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span><span class="p">]</span> <span class="bp">self</span><span class="o">.</span><span class="n">notify_observers</span><span class="p">()</span> <span class="k">def</span> <span class="nf">upshift</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span> <span class="o">+=</span> <span class="mi">1</span> <span class="bp">self</span><span class="o">.</span><span class="n">speed</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">rpm</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span><span class="p">]</span> <span class="bp">self</span><span class="o">.</span><span class="n">notify_observers</span><span class="p">()</span> </pre> </div> </div> <div class="tab-pane" id="code-6"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Car</span><span class="p">(</span><span class="n">Observable</span><span class="p">):</span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">Observable</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">speed</span> <span class="o">=</span> <span class="mi">0</span> <span class="bp">self</span><span class="o">.</span><span class="n">rpm</span> <span class="o">=</span> <span class="mi">0</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox</span> <span class="o">=</span> <span class="p">[</span><span class="o">.</span><span class="mo">016</span><span class="p">,</span> <span class="mf">0.038</span><span class="p">,</span> <span class="o">.</span><span class="mo">03</span><span class="mi">8</span><span class="p">,</span> <span class="o">.</span><span class="mo">042</span><span class="p">]</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">def</span> <span class="nf">accelerate</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">rpm</span> <span class="o">+=</span> <span class="mi">1000</span> <span class="bp">self</span><span class="o">.</span><span class="n">speed</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">rpm</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span><span class="p">]</span> <span class="bp">self</span><span class="o">.</span><span class="n">notify_observers</span><span class="p">()</span> <span class="k">def</span> <span class="nf">downshift</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span> <span class="o">-=</span> <span class="mi">1</span> <span class="bp">self</span><span class="o">.</span><span class="n">speed</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">rpm</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span><span class="p">]</span> <span class="bp">self</span><span class="o">.</span><span class="n">notify_observers</span><span class="p">()</span> <span class="k">def</span> <span class="nf">upshift</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span> <span class="o">+=</span> <span class="mi">1</span> <span class="bp">self</span><span class="o">.</span><span class="n">speed</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">rpm</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span><span class="p">]</span> <span class="bp">self</span><span class="o">.</span><span class="n">notify_observers</span><span class="p">()</span> </pre> </div> </div> </div> <p>Voici comment ça s'utilise dans le code.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-7">Python 3 </a></li> <li><a class="reference internal" href="#code-8">Python 2 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-7"> <div class="highlight"> <pre class="literal-block"> <span class="n">car</span> <span class="o">=</span> <span class="n">Car</span><span class="p">()</span> <span class="n">car</span><span class="o">.</span><span class="n">add_observer</span><span class="p">(</span><span class="n">Speed</span><span class="p">())</span> <span class="n">car</span><span class="o">.</span><span class="n">add_observer</span><span class="p">(</span><span class="n">Rpm</span><span class="p">())</span> <span class="n">car</span><span class="o">.</span><span class="n">accelerate</span><span class="p">()</span> <span class="n">car</span><span class="o">.</span><span class="n">upshift</span><span class="p">()</span> <span class="n">car</span><span class="o">.</span><span class="n">upshift</span><span class="p">()</span> </pre> </div> </div> <div class="tab-pane" id="code-8"> <div class="highlight"> <pre class="literal-block"> <span class="n">car</span> <span class="o">=</span> <span class="n">Car</span><span class="p">()</span> <span class="n">car</span><span class="o">.</span><span class="n">add_observer</span><span class="p">(</span><span class="n">Speed</span><span class="p">())</span> <span class="n">car</span><span class="o">.</span><span class="n">add_observer</span><span class="p">(</span><span class="n">Rpm</span><span class="p">())</span> <span class="n">car</span><span class="o">.</span><span class="n">accelerate</span><span class="p">()</span> <span class="n">car</span><span class="o">.</span><span class="n">upshift</span><span class="p">()</span> <span class="n">car</span><span class="o">.</span><span class="n">upshift</span><span class="p">()</span> </pre> </div> </div> </div> <p>On peut faire plusieurs remarques.Les observeurs prennent en paramètre l'objet Car et vont chercher se dont il on besoin.On appelle ceci le mode pull qui est à l'opposer du modèle push où le sujet envoie des informations détaillées sur son état généralement dans un objet appelé Event.Lorsque l'on passe un rapport avec la méthode upshift seule la vitesse (speed) change; pourtant, tout les observer sont notifiés. Une solution et de rendre Observable plus complexe en lui permettant d'enregistrer un observer sur certain évènement comme un changement de rpm ou de vitesse.</p> <p>Voici les modifications de la classe Observable pour que les notifications soie plus sélective.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-9">Python 3 </a></li> <li><a class="reference internal" href="#code-10">Python 2 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-9"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Observable</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">observers</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="nb">list</span><span class="p">)</span> <span class="c1"># Pour un évènement en clef,</span> <span class="c1"># j'ai une liste d'observers.</span> <span class="k">def</span> <span class="nf">notify_observers</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">event</span><span class="p">):</span> <span class="k">for</span> <span class="n">obs</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">observers</span><span class="p">[</span><span class="n">event</span><span class="p">]:</span> <span class="n">obs</span><span class="o">.</span><span class="n">notify</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="k">def</span> <span class="nf">add_observer</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obs</span><span class="p">,</span> <span class="n">event</span><span class="p">):</span> <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">obs</span><span class="p">,</span> <span class="s1">'notify'</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;First argument must be object with notify method&quot;</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">observers</span><span class="p">[</span><span class="n">event</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">obs</span><span class="p">)</span> <span class="k">class</span> <span class="nc">Car</span><span class="p">(</span><span class="n">Observable</span><span class="p">):</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">Observable</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">speed</span> <span class="o">=</span> <span class="mi">0</span> <span class="bp">self</span><span class="o">.</span><span class="n">rpm</span> <span class="o">=</span> <span class="mi">0</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox</span> <span class="o">=</span> <span class="p">[</span><span class="o">.</span><span class="mi">016</span><span class="p">,</span> <span class="mf">0.038</span><span class="p">,</span> <span class="o">.</span><span class="mi">038</span><span class="p">,</span> <span class="o">.</span><span class="mi">042</span><span class="p">]</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">def</span> <span class="nf">accelerate</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">rpm</span> <span class="o">+=</span> <span class="mi">1000</span> <span class="bp">self</span><span class="o">.</span><span class="n">speed</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">rpm</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span><span class="p">]</span> <span class="bp">self</span><span class="o">.</span><span class="n">notify_observers</span><span class="p">(</span><span class="s1">'rpm'</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">notify_observers</span><span class="p">(</span><span class="s1">'speed'</span><span class="p">)</span> <span class="k">def</span> <span class="nf">downshift</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span> <span class="o">-=</span> <span class="mi">1</span> <span class="bp">self</span><span class="o">.</span><span class="n">speed</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">rpm</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span><span class="p">]</span> <span class="bp">self</span><span class="o">.</span><span class="n">notify_observers</span><span class="p">(</span><span class="s1">'speed'</span><span class="p">)</span> <span class="c1"># Seul les observer de la vitesse</span> <span class="c1"># sont notifier.</span> <span class="k">def</span> <span class="nf">upshift</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span> <span class="o">+=</span> <span class="mi">1</span> <span class="bp">self</span><span class="o">.</span><span class="n">speed</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">rpm</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span><span class="p">]</span> <span class="bp">self</span><span class="o">.</span><span class="n">notify_observers</span><span class="p">(</span><span class="s1">'speed'</span><span class="p">)</span> <span class="n">car</span> <span class="o">=</span> <span class="n">Car</span><span class="p">()</span> <span class="n">car</span><span class="o">.</span><span class="n">add_observer</span><span class="p">(</span><span class="n">Speed</span><span class="p">(),</span> <span class="s1">'speed'</span><span class="p">)</span> <span class="n">car</span><span class="o">.</span><span class="n">add_observer</span><span class="p">(</span><span class="n">Rpm</span><span class="p">(),</span><span class="s1">'rpm'</span><span class="p">)</span> </pre> </div> </div> <div class="tab-pane" id="code-10"> <div class="highlight"> <pre class="literal-block"> <span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">defaultdict</span> <span class="k">class</span> <span class="nc">Observable</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">observers</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="nb">list</span><span class="p">)</span> <span class="c1"># Pour un évènement en clef,</span> <span class="c1"># j'ai une liste d'observers.</span> <span class="k">def</span> <span class="nf">notify_observers</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">event</span><span class="p">):</span> <span class="k">for</span> <span class="n">obs</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">observers</span><span class="p">[</span><span class="n">event</span><span class="p">]:</span> <span class="n">obs</span><span class="o">.</span><span class="n">notify</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="k">def</span> <span class="nf">add_observer</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obs</span><span class="p">,</span> <span class="n">event</span><span class="p">):</span> <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">obs</span><span class="p">,</span> <span class="s1">'notify'</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;First argument must be object with notify method&quot;</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">observers</span><span class="p">[</span><span class="n">event</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">obs</span><span class="p">)</span> <span class="k">class</span> <span class="nc">Car</span><span class="p">(</span><span class="n">Observable</span><span class="p">):</span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">Observable</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">speed</span> <span class="o">=</span> <span class="mi">0</span> <span class="bp">self</span><span class="o">.</span><span class="n">rpm</span> <span class="o">=</span> <span class="mi">0</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox</span> <span class="o">=</span> <span class="p">[</span><span class="o">.</span><span class="mo">016</span><span class="p">,</span> <span class="mf">0.038</span><span class="p">,</span> <span class="o">.</span><span class="mo">03</span><span class="mi">8</span><span class="p">,</span> <span class="o">.</span><span class="mo">042</span><span class="p">]</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">def</span> <span class="nf">accelerate</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">rpm</span> <span class="o">+=</span> <span class="mi">1000</span> <span class="bp">self</span><span class="o">.</span><span class="n">speed</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">rpm</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span><span class="p">]</span> <span class="bp">self</span><span class="o">.</span><span class="n">notify_observers</span><span class="p">(</span><span class="s1">'rpm'</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">notify_observers</span><span class="p">(</span><span class="s1">'speed'</span><span class="p">)</span> <span class="k">def</span> <span class="nf">downshift</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span> <span class="o">-=</span> <span class="mi">1</span> <span class="bp">self</span><span class="o">.</span><span class="n">speed</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">rpm</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span><span class="p">]</span> <span class="bp">self</span><span class="o">.</span><span class="n">notify_observers</span><span class="p">(</span><span class="s1">'speed'</span><span class="p">)</span> <span class="c1"># Seul les observer de la vitesse</span> <span class="c1"># sont notifier.</span> <span class="k">def</span> <span class="nf">upshift</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span> <span class="o">+=</span> <span class="mi">1</span> <span class="bp">self</span><span class="o">.</span><span class="n">speed</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">rpm</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">gearbox</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">gearbox_speed</span><span class="p">]</span> <span class="bp">self</span><span class="o">.</span><span class="n">notify_observers</span><span class="p">(</span><span class="s1">'speed'</span><span class="p">)</span> <span class="n">car</span> <span class="o">=</span> <span class="n">Car</span><span class="p">()</span> <span class="n">car</span><span class="o">.</span><span class="n">add_observer</span><span class="p">(</span><span class="n">Speed</span><span class="p">(),</span> <span class="s1">'speed'</span><span class="p">)</span> <span class="n">car</span><span class="o">.</span><span class="n">add_observer</span><span class="p">(</span><span class="n">Rpm</span><span class="p">(),</span><span class="s1">'rpm'</span><span class="p">)</span> </pre> </div> </div> </div> <div class="section" id="les-observers-threades"> <h2>Les observers threadés</h2> <p>On va s'attaquer à un problème plus complexe. Lorsque l'état d'un objet est modifié, ses observer vont mettre à jour une BDD, envoyer un mail ou uploader un fichier. Le problème et que si l'un des observer met beaucoup de temps à faire son travail, il va bloquer touts les autres. Pour éviter ça, on va utiliser des threads.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-11">Python 3 </a></li> <li><a class="reference internal" href="#code-12">Python 2 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-11"> <div class="highlight"> <pre class="literal-block"> <span class="kn">from</span> <span class="nn">time</span> <span class="k">import</span> <span class="n">sleep</span> <span class="k">class</span> <span class="nc">SendMail</span><span class="p">:</span> <span class="k">def</span> <span class="nf">notify</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">subject</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="s1">'start send mail'</span><span class="p">)</span> <span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c1"># Simule un long envoie de mail.</span> <span class="nb">print</span><span class="p">(</span><span class="s1">'end send mail'</span><span class="p">)</span> <span class="k">class</span> <span class="nc">UploadFile</span><span class="p">:</span> <span class="k">def</span> <span class="nf">notify</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">subject</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="s1">'start upload'</span><span class="p">)</span> <span class="n">sleep</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="c1"># Simule l'upload d'un gros fichier.</span> <span class="nb">print</span><span class="p">(</span><span class="s1">'end upload'</span><span class="p">)</span> <span class="kn">from</span> <span class="nn">threading</span> <span class="k">import</span> <span class="n">Thread</span> <span class="k">class</span> <span class="nc">Observable</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">observers</span> <span class="o">=</span> <span class="p">[]</span> <span class="k">def</span> <span class="nf">notify_observers</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">for</span> <span class="n">obs</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">observers</span><span class="p">:</span> <span class="c1"># La notification se fait dans</span> <span class="c1"># un thread séparé.</span> <span class="n">thread</span> <span class="o">=</span> <span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">obs</span><span class="o">.</span><span class="n">notify</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="bp">self</span><span class="p">,))</span> <span class="n">thread</span><span class="o">.</span><span class="n">start</span><span class="p">()</span> <span class="k">def</span> <span class="nf">add_observer</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obs</span><span class="p">):</span> <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">obs</span><span class="p">,</span> <span class="s1">'notify'</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;First argument must be object with notify method&quot;</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">observers</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">obs</span><span class="p">)</span> <span class="n">subject</span> <span class="o">=</span> <span class="n">Observable</span><span class="p">()</span> <span class="n">subject</span><span class="o">.</span><span class="n">add_observer</span><span class="p">(</span><span class="n">SendMail</span><span class="p">())</span> <span class="n">subject</span><span class="o">.</span><span class="n">add_observer</span><span class="p">(</span><span class="n">UploadFile</span><span class="p">())</span> <span class="n">subject</span><span class="o">.</span><span class="n">notify_observers</span><span class="p">()</span> <span class="n">subject</span><span class="o">.</span><span class="n">notify_observers</span><span class="p">()</span> </pre> </div> </div> <div class="tab-pane" id="code-12"> <div class="highlight"> <pre class="literal-block"> <span class="kn">from</span> <span class="nn">time</span> <span class="kn">import</span> <span class="n">sleep</span> <span class="k">class</span> <span class="nc">SendMail</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="k">def</span> <span class="nf">notify</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">subject</span><span class="p">):</span> <span class="k">print</span> <span class="s1">'start send mail'</span> <span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c1"># Simule un long envoie de mail.</span> <span class="k">print</span> <span class="s1">'end send mail'</span> <span class="k">class</span> <span class="nc">UploadFile</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="k">def</span> <span class="nf">notify</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">subject</span><span class="p">):</span> <span class="k">print</span> <span class="s1">'start upload'</span> <span class="n">sleep</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="c1"># Simule l'upload d'un gros fichier.</span> <span class="k">print</span> <span class="s1">'end upload'</span> <span class="kn">from</span> <span class="nn">threading</span> <span class="kn">import</span> <span class="n">Thread</span> <span class="k">class</span> <span class="nc">Observable</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">observers</span> <span class="o">=</span> <span class="p">[]</span> <span class="k">def</span> <span class="nf">notify_observers</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">for</span> <span class="n">obs</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">observers</span><span class="p">:</span> <span class="c1"># La notification se fait dans</span> <span class="c1"># un thread séparé.</span> <span class="n">thread</span> <span class="o">=</span> <span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">obs</span><span class="o">.</span><span class="n">notify</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="bp">self</span><span class="p">,))</span> <span class="n">thread</span><span class="o">.</span><span class="n">start</span><span class="p">()</span> <span class="k">def</span> <span class="nf">add_observer</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obs</span><span class="p">):</span> <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">obs</span><span class="p">,</span> <span class="s1">'notify'</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;First argument must be object with notify method&quot;</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">observers</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">obs</span><span class="p">)</span> <span class="n">subject</span> <span class="o">=</span> <span class="n">Observable</span><span class="p">()</span> <span class="n">subject</span><span class="o">.</span><span class="n">add_observer</span><span class="p">(</span><span class="n">SendMail</span><span class="p">())</span> <span class="n">subject</span><span class="o">.</span><span class="n">add_observer</span><span class="p">(</span><span class="n">UploadFile</span><span class="p">())</span> <span class="n">subject</span><span class="o">.</span><span class="n">notify_observers</span><span class="p">()</span> <span class="n">subject</span><span class="o">.</span><span class="n">notify_observers</span><span class="p">()</span> </pre> </div> </div> </div> <p>Voilà j'espère que ça vous a plu n'hésitez pas si vous avez des remarques d'ici là a vos IDE et bon code ! <img alt="smile" src="/theme/img/smiley/smiley-smile.gif" /></p> </div> Venv, le nouveau Virtualenv2014-09-07T12:03:00+02:002014-09-07T12:03:00+02:00Vincent Mailloltag:autourducode.com,2014-09-07:/venv-le-nouveau-virtualenv.html<p class="first last">Virtualenv est un outil qui permet de gérer plusieurs environnements python isolés les uns des autres.</p> <p><strong>Virtualenv</strong> est un outil qui permet de gérer plusieurs environnements python isolés les uns des autres.</p> <p>Chaque environnement possède donc ses propres packages et vous pouvez également avoir des versions de python différentes d'un environnement à l'autre.</p> <p>La <a class="reference external" href="https://www.python.org/dev/peps/pep-0405/">PEP 405</a> a proposé d'intégrer ce mécanisme dans python et ce fut chose faites dans Python 3.3 avec l'intégration du module venv. Voici comment ça marche: Imaginons que l'on doit faire un site web sur M. Michu en utilisant Django et une application web pour la commande de sushi en utilisant Tornado. On va d'abord créer deux environnements en utilisant la commande <strong>python3 -m venv</strong></p> <p><kbd class="kbd"> $ python3 -m venv michuWebSite</kbd> </p> <p><kbd class="kbd"> $ python3 -m venv sushiWebSite</kbd> </p> <p>Ensuite on active l'environnement avec lequel on veux travailler. en utilisant un des script créé dans &lt;votre venv&gt;/bin. Activate pour bash, activate.bat sous windows ou Activate.ps1 si vous utilisez PowerShell.</p> <p><kbd class="kbd"> $ source sushiWebSite/bin/activate</kbd> </p> <p>On peut ensuite utiliser <strong>pip</strong> pour installer les dépendances du projet. Si vous êtes sous python 3.3 et que votre installation cotoie une python 2.7 un problème peu se poser</p> <p><kbd class="kbd"> $ pip install tornado</kbd> </p> <pre class="literal-block"> Downloading/unpacking tornado Downloading tornado-4.0.1.tar.gz (314kB): 314kB downloaded Running setup.py egg_info for package tornado ... creating /usr/lib64/python2.7/site-packages/tornado error: could not create '/usr/lib64/python2.7/site-packages/tornado': Permission denied </pre> <p>C'est le pip de python 2.7 qui est appelé pas celui de notre venv. On va donc installer pip dans notre venv. Pour ce faire, on va télécharger le script <strong>get-pip.py</strong></p> <p><kbd class="kbd"> $ curl https://raw.githubusercontent.com/pypa/pip/master/contrib/get-pip.py &gt; get-pip.py</kbd> </p> <pre class="literal-block"> % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 1309k 100 1309k 0 0 779k 0 0:00:01 0:00:01 --:--:-- 779k </pre> <p>Puis l'installer et créer un wrapper dans &lt;venv&gt;/bin/</p> <p><kbd class="kbd"> (sushiWebSite) $ python get-pip.py</kbd> </p> <pre class="literal-block"> Downloading/unpacking pip ... Cleaning up... (sushiWebSite) [maillol&#64;new-host ~]$ echo 'python -m pip &quot;$&#64;&quot;' &gt; sushiWebSite/bin/pip </pre> <p>On sort de notre venv en utilisant deactivate puis on le réactive.</p> <p><kbd class="kbd"> (sushiWebSite) $ deactivate</kbd> </p> <pre class="literal-block"> $ source sushiWebSite/bin/activate </pre> <p>Maintenant pas de problème pour installer tornado.</p> <p><kbd class="kbd"> (sushiWebSite) $ pip install tornado</kbd> </p> <pre class="literal-block"> Downloading/unpacking tornado ... Successfully installed tornado certifi Cleaning up... </pre> <p>Tornado est donc bien installé dans ce venv uniquement celui-ci.</p> <p><kbd class="kbd"> (sushiWebSite) $ python -c 'import tornado'</kbd> </p> <p>Si l'on change de venv tornado ne si trouve plus.</p> <p><kbd class="kbd"> (sushiWebSite) $ deactivate</kbd> </p> <p><kbd class="kbd"> $ source michuWebSite/bin/activate</kbd> </p> <p><kbd class="kbd"> (michuWebSite) $ python -c 'import tornado'</kbd> </p> <pre class="literal-block"> Traceback (most recent call last): File &quot;&lt;string&gt;&quot;, line 1, in &lt;module&gt; ImportError: No module named 'tornado' </pre> <p>Notez que la version 3.4 de python intègre pip, donc vous n'aurez pas à l'installer. Autre chose, vous n'etez pas obliger de mettre le code de votre projet dans votre venv vous pouvez très bien avoir un répertoire dans lequel vous géré tout vos venv et votre workspace avec vos source python à coté, ça peut être pratique si deux projets doivent tourner sur le même venv</p> <p>Allez à vos IDE et bon code! <img alt="smile" src="/theme/img/smiley/smiley-smile.gif" /></p> Les classes mixins2014-08-29T22:33:00+02:002014-08-29T22:33:00+02:00Vincent Mailloltag:autourducode.com,2014-08-29:/les-classes-mixins.html<p class="first last">Les mixins sont des classes vouées à être assemblées entre elles pour créer d'autre classe par héritage. Cela peut paraitre simple, mais ça peut vite être piégeux.</p> <p>Les mixins sont des classes vouées à être assemblées entre elles pour créer d'autre classe par héritage. Cela peut paraitre simple, mais ça peut vite être piégeux.</p> <p>Premier piège, ma mixin à besoin d'attributs d'instance pour fonctionner.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">FooMixin</span><span class="p">:</span> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">foo_attr</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="mi">33</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">foo_attr</span><span class="p">)</span> <span class="k">class</span> <span class="nc">BarMixin</span><span class="p">:</span> <span class="k">def</span> <span class="nf">bar</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">bar_attr</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="mi">21</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">bar_attr</span><span class="p">)</span> <span class="k">class</span> <span class="nc">FooBar</span><span class="p">(</span><span class="n">FooMixin</span><span class="p">,</span> <span class="n">BarMixin</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot; FooBar class &quot;&quot;&quot;</span> <span class="n">FooBar</span><span class="p">()</span><span class="o">.</span><span class="n">foo</span><span class="p">()</span> <span class="c1"># AttributeError: 'FooBar' object has no attribute 'foo_attr'</span> </pre> </div> </div> </div> <p>On pourrait créer un constructeur dans FooMixin et BarMixin pour initialiser foo_attr et bar_attr, mais cela obligerai l'utilisateur des mixins à les appeler dans sa classe FooBar</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-2">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">FooBar</span><span class="p">(</span><span class="n">FooMixin</span><span class="p">,</span> <span class="n">BarMixin</span><span class="p">):</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">FooMixin</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="n">BarMixin</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> </pre> </div> </div> </div> <p>Une solution pour simplifier la vie de notre utilisateur et de regarder si l'attribut existe et s'il n'existe pas on, le crée.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-3">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">FooMixin</span><span class="p">:</span> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s1">'foo_attr'</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">foo_attr</span> <span class="o">=</span> <span class="p">[]</span> <span class="bp">self</span><span class="o">.</span><span class="n">foo_attr</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="mi">33</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">foo_attr</span><span class="p">)</span> <span class="k">class</span> <span class="nc">BarMixin</span><span class="p">:</span> <span class="k">def</span> <span class="nf">bar</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s1">'bar_attr'</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">bar_attr</span> <span class="o">=</span> <span class="p">[]</span> <span class="bp">self</span><span class="o">.</span><span class="n">bar_attr</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="mi">21</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">bar_attr</span><span class="p">)</span> <span class="k">class</span> <span class="nc">FooBar</span><span class="p">(</span><span class="n">FooMixin</span><span class="p">,</span> <span class="n">BarMixin</span><span class="p">):</span> <span class="k">pass</span> <span class="n">f</span> <span class="o">=</span> <span class="n">FooBar</span><span class="p">()</span> <span class="n">f</span><span class="o">.</span><span class="n">foo</span><span class="p">()</span> <span class="c1"># [33]</span> <span class="n">f</span><span class="o">.</span><span class="n">foo</span><span class="p">()</span> <span class="c1"># [33, 33]</span> </pre> </div> </div> </div> <p>Une dernière chose... Si vous souhaitez éviter ceci:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-4">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">FooBar</span><span class="p">(</span><span class="n">FooMixin</span><span class="p">,</span> <span class="n">BarMixin</span><span class="p">):</span> <span class="n">foo_attr</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">f</span> <span class="o">=</span> <span class="n">FooBar</span><span class="p">()</span> <span class="n">f</span><span class="o">.</span><span class="n">foo</span><span class="p">()</span> <span class="c1"># AttributeError: 'int' object has no attribute 'append'</span> </pre> </div> </div> </div> <p>Et que vous ne souhaitez pas que l'utilisateur puisse initialiser l'attribut il suffit de le préfixer de deux underscores.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-5">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-5"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">FooMixin</span><span class="p">:</span> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s1">'_FooMixin__foo_attr'</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">__foo_attr</span> <span class="o">=</span> <span class="p">[]</span> <span class="bp">self</span><span class="o">.</span><span class="n">__foo_attr</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="mi">33</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">__foo_attr</span><span class="p">)</span> </pre> </div> </div> </div> <p>Pour finir, voyons un cas plus complexe, le conflit entre les noms des méthodes. Imaginions que l'on souhaite faire un jeu avec des combats entre créatures. Les créatures ont des points de vie et de force.Il y a une mixin vampire la créature regagne de la vie quand elle attaque, puis une mixin Apprenti, a chaque attaque la créature gagne en force.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-6">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-6"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Apprenti</span><span class="p">:</span> <span class="k">def</span> <span class="nf">attaquer</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span> <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s2">&quot;_Croissant__extra&quot;</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">__extra</span> <span class="o">=</span> <span class="mi">0</span> <span class="n">other</span><span class="o">.</span><span class="n">vie</span> <span class="o">-=</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">force</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">__extra</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">__extra</span> <span class="o">+=</span> <span class="mi">2</span> <span class="k">class</span> <span class="nc">Vampire</span><span class="p">:</span> <span class="k">def</span> <span class="nf">attaquer</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span> <span class="n">vie_avant</span> <span class="o">=</span> <span class="n">other</span><span class="o">.</span><span class="n">vie</span> <span class="n">other</span><span class="o">.</span><span class="n">vie</span> <span class="o">-=</span> <span class="bp">self</span><span class="o">.</span><span class="n">force</span> <span class="bp">self</span><span class="o">.</span><span class="n">vie</span> <span class="o">+=</span> <span class="p">(</span><span class="n">vie_avant</span> <span class="o">-</span> <span class="n">other</span><span class="o">.</span><span class="n">vie</span><span class="p">)</span> <span class="o">//</span> <span class="mi">2</span> </pre> </div> </div> </div> <p>Le problème et que si l'on compose une créature avec c'est deux classes, c'est soie la méthode attaquer de Vampier soie celle d'Apprenti qui sera appelée. Or on veut une créature qui fait les deux.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-7">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-7"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">ApprentiVampire</span><span class="p">(</span><span class="n">Apprenti</span><span class="p">,</span> <span class="n">Vampire</span><span class="p">):</span> <span class="s2">&quot;Se comporte comme un Apprenti&quot;</span> <span class="k">class</span> <span class="nc">ApprentiVampire</span><span class="p">(</span><span class="n">Vampire</span><span class="p">,</span> <span class="n">Apprenti</span><span class="p">):</span> <span class="s2">&quot;Se comporte comme un Vampire&quot;</span> </pre> </div> </div> </div> <p>La solution et que la mixin regarde dans les classes parents si elle trouve une autre méthode attaquer et de l'appeler au bon moment, par contre on va se retrouver avec des appels sans fin si l'on fait ça n'importe comment. Voici comment on peut s'y prendre:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-8">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-8"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Apprenti</span><span class="p">:</span> <span class="k">def</span> <span class="nf">attaquer</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span> <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s2">&quot;_Apprenti__extra&quot;</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">__extra</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1"># Je prends le tuple des classes parents</span> <span class="n">parents</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="vm">__bases__</span> <span class="c1"># Je ne regarde que les classes après moi dans le tuple</span> <span class="n">i</span> <span class="o">=</span> <span class="n">parents</span><span class="o">.</span><span class="n">index</span><span class="p">(</span><span class="n">Apprenti</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span> <span class="k">for</span> <span class="n">parent</span> <span class="ow">in</span> <span class="n">parents</span><span class="p">[</span><span class="n">i</span><span class="p">:]:</span> <span class="c1"># Si une classe a une méthode attaquée</span> <span class="c1"># j'exécute sa méthode.</span> <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">parent</span><span class="p">,</span> <span class="s1">'attaquer'</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">force</span> <span class="o">+=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__extra</span> <span class="n">parent</span><span class="o">.</span><span class="n">attaquer</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">force</span> <span class="o">-=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__extra</span> <span class="k">break</span> <span class="k">else</span><span class="p">:</span> <span class="n">other</span><span class="o">.</span><span class="n">vie</span> <span class="o">-=</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">force</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">__extra</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">__extra</span> <span class="o">+=</span> <span class="mi">2</span> <span class="k">class</span> <span class="nc">Vampire</span><span class="p">:</span> <span class="k">def</span> <span class="nf">attaquer</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span> <span class="n">vie_avant</span> <span class="o">=</span> <span class="n">other</span><span class="o">.</span><span class="n">vie</span> <span class="n">parents</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="vm">__bases__</span> <span class="n">i</span> <span class="o">=</span> <span class="n">parents</span><span class="o">.</span><span class="n">index</span><span class="p">(</span><span class="n">Vampire</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span> <span class="k">for</span> <span class="n">parent</span> <span class="ow">in</span> <span class="n">parents</span><span class="p">[</span><span class="n">i</span><span class="p">:]:</span> <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">parent</span><span class="p">,</span> <span class="s1">'attaquer'</span><span class="p">):</span> <span class="n">parent</span><span class="o">.</span><span class="n">attaquer</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">)</span> <span class="k">break</span> <span class="k">else</span><span class="p">:</span> <span class="n">other</span><span class="o">.</span><span class="n">vie</span> <span class="o">-=</span> <span class="bp">self</span><span class="o">.</span><span class="n">force</span> <span class="bp">self</span><span class="o">.</span><span class="n">vie</span> <span class="o">+=</span> <span class="p">(</span><span class="n">vie_avant</span> <span class="o">-</span> <span class="n">other</span><span class="o">.</span><span class="n">vie</span><span class="p">)</span> <span class="o">//</span> <span class="mi">2</span> <span class="k">class</span> <span class="nc">ApprentiVampire</span><span class="p">(</span><span class="n">Apprenti</span><span class="p">,</span> <span class="n">Vampire</span><span class="p">):</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">force</span> <span class="o">=</span> <span class="mi">10</span> <span class="bp">self</span><span class="o">.</span><span class="n">vie</span> <span class="o">=</span> <span class="mi">20</span> <span class="k">def</span> <span class="nf">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="s2">&quot;force:</span><span class="si">%s</span><span class="s2"> vie:</span><span class="si">%s</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">force</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">vie</span><span class="p">)</span> <span class="k">class</span> <span class="nc">SimpleCreature</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">vie</span> <span class="o">=</span> <span class="mi">30</span> <span class="n">vampire</span> <span class="o">=</span> <span class="n">ApprentiVampire</span><span class="p">()</span> <span class="n">creature</span> <span class="o">=</span> <span class="n">SimpleCreature</span><span class="p">()</span> <span class="nb">print</span><span class="p">(</span><span class="n">vampire</span><span class="o">.</span><span class="n">vie</span><span class="p">,</span> <span class="n">creature</span><span class="o">.</span><span class="n">vie</span><span class="p">)</span> <span class="c1"># 20 30</span> <span class="n">vampire</span><span class="o">.</span><span class="n">attaquer</span><span class="p">(</span><span class="n">creature</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="n">vampire</span><span class="o">.</span><span class="n">vie</span><span class="p">,</span> <span class="n">creature</span><span class="o">.</span><span class="n">vie</span><span class="p">)</span> <span class="c1"># 25 20</span> <span class="n">vampire</span><span class="o">.</span><span class="n">attaquer</span><span class="p">(</span><span class="n">creature</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="n">vampire</span><span class="o">.</span><span class="n">vie</span><span class="p">,</span> <span class="n">creature</span><span class="o">.</span><span class="n">vie</span><span class="p">)</span> <span class="c1"># 31 8</span> <span class="n">vampire</span><span class="o">.</span><span class="n">attaquer</span><span class="p">(</span><span class="n">creature</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="n">vampire</span><span class="o">.</span><span class="n">vie</span><span class="p">,</span> <span class="n">creature</span><span class="o">.</span><span class="n">vie</span><span class="p">)</span> <span class="c1"># 38 -6</span> </pre> </div> </div> </div> <p>Pour conclure, les mixin peuvent vous permette de créer une libraire sympa où l'utilisateur aura juste à assembler des classes pour avoir une classe sur-mesure. L'inconvénient c'est que c'est pas mal de taf en amont et que c'est statique. Si les comportements ont besoin d'évoluer dynamiquement, il vaut mieux envisager le design pattern décorateur.</p> <p>J'espère que ça vous a plu alors à vos IDE et bon code ! <img alt="smile" src="/theme/img/smiley/smiley-smile.gif" /></p> Les descripteurs2014-08-11T21:13:00+02:002014-08-11T21:13:00+02:00Vincent Mailloltag:autourducode.com,2014-08-11:/les-descripteurs.html<p class="first last">Un descripteur permet de définir des comportements lorsque l'on souhaite accèder à un attribut d'une classe.</p> <p>Un descripteur permet de définir des comportements lorsque l'on souhaite accèder à un attribut d'une classe.</p> <p>Un descripteur peut définir trois méthodes:</p> <ul class="simple"> <li>__get__ qui sera appelé lorsque l'on accède à l'attribut d'un objet (obj.attr)</li> <li>__set__ qui sera appelé lorsque l'on modifie l'attribut (obj.attr = truc) et</li> <li>__delete__ qui sera appelé lorsque l'on veux supprimer un attribut via del</li> </ul> <p>Voici un exemple de descripteur qui ne fait rien d'autre que d'afficher l'appel à ses méthodes __get__, __set__ et __delete__.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">MyDescriptor</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="bp">cls</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;__get__&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="nb">type</span><span class="p">)</span> <span class="k">def</span> <span class="nf">__set__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;__set__&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span> <span class="k">def</span> <span class="nf">__delete__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;__del__&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="p">)</span> </pre> </div> </div> </div> <p>Je crée ensuite une classe A dans laquelle je définis un attribut qui contiendra une instance de mon descripteur.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-2">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">A</span><span class="p">:</span> <span class="n">my_attr</span> <span class="o">=</span> <span class="n">MyDescriptor</span><span class="p">()</span> </pre> </div> </div> </div> <p>Maintenant, tout accès, modification ou suppression de l'attribut my_attr d'une instance de A déclenchera un appel à la méthode appropriée du descripteur.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-3">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="n">a</span> <span class="o">=</span> <span class="n">A</span><span class="p">()</span> <span class="n">a</span><span class="o">.</span><span class="n">my_attr</span> <span class="c1"># __get__ &lt;__main__.a object=&quot;&quot; at=&quot;&quot; 0x7f912c0e9f50=&quot;&quot;&gt; &lt;class __main__=&quot;&quot; a=&quot;&quot;&gt;</span> <span class="n">a</span><span class="o">.</span><span class="n">my_attr</span> <span class="o">=</span> <span class="mi">33</span> <span class="c1"># __set__ &lt;__main__.a object=&quot;&quot; at=&quot;&quot; 0x7f912c0e9f50=&quot;&quot;&gt; 33</span> <span class="k">del</span> <span class="n">a</span><span class="o">.</span><span class="n">my_attr</span> <span class="c1"># __del__ &lt;__main__.a object=&quot;&quot; at=&quot;&quot; 0x7f912c0e9f50=&quot;&quot;&gt;&lt;!--__main__.a--&gt;&lt;!--__main__.a--&gt;&lt;/class&gt;&lt;!--__main__.a--&gt;</span> </pre> </div> </div> </div> <p>Un descripteur bien connu est <strong>property</strong>. Il vous demande quelle méthode vous souhaitez utiliser pour accéder, modifier ou supprimer un attribut. Si vous ne spécifier pas de methode pour une action, cette action ne sera pas autorisé.Voici l'exemple d'une classe où x n'est accessible qu'en lecture.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-4">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">ClassX</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_x</span> <span class="o">=</span> <span class="mi">42</span> <span class="c1"># Je crée un attribut privé.</span> <span class="k">def</span> <span class="nf">get_x</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="c1"># Une méthode pour accéder à mon attribut.</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_x</span> <span class="n">x</span> <span class="o">=</span> <span class="nb">property</span><span class="p">(</span><span class="n">get_x</span><span class="p">)</span> <span class="c1"># J'indique que get_x est la méthode</span> <span class="c1"># pour accéder à x.</span> <span class="n">obj_x</span> <span class="o">=</span> <span class="n">ClassX</span><span class="p">()</span> <span class="nb">print</span><span class="p">(</span><span class="n">obj_x</span><span class="o">.</span><span class="n">x</span><span class="p">)</span> <span class="c1"># 42</span> <span class="c1"># obj_x.x = 33 # AttributeError: can't set attribute</span> </pre> </div> </div> </div> <p>Cela peut aussi s'utiliser en décorant la méthode.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-5">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-5"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">ClassX</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_x</span> <span class="o">=</span> <span class="mi">42</span> <span class="nd">&#64;property</span> <span class="k">def</span> <span class="nf">x</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_x</span> </pre> </div> </div> </div> <p>Passon aux choses sérieuses, on va écrire une classe Personne qui instanciera un objet à partir d'une ligne lu dans un fichier tabulé. Chaque ligne du fichier tabulé ce présente comme ceci:</p> <p>prenom nom age</p> <p>Chaque champ du fichier deviendra un attribut et on utilisera des property pour vérifier est transtyper les attributs.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-6">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-6"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Personne</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">line</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_fields</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">'</span><span class="se">\t</span><span class="s1">'</span><span class="p">)</span> <span class="nd">&#64;property</span> <span class="k">def</span> <span class="nf">prenom</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_fields</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="nd">&#64;prenom</span><span class="o">.</span><span class="n">setter</span> <span class="k">def</span> <span class="nf">prenom</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_fields</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span> <span class="nd">&#64;property</span> <span class="k">def</span> <span class="nf">nom</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_fields</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="nd">&#64;nom</span><span class="o">.</span><span class="n">setter</span> <span class="k">def</span> <span class="nf">nom</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_fields</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span> <span class="nd">&#64;property</span> <span class="k">def</span> <span class="nf">age</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="nb">int</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_fields</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span> <span class="nd">&#64;age</span><span class="o">.</span><span class="n">setter</span> <span class="k">def</span> <span class="nf">age</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_fields</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="k">def</span> <span class="nf">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="s2">&quot;</span><span class="se">\t</span><span class="s2">&quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_fields</span><span class="p">)</span> </pre> </div> </div> </div> <p>Comme on peut le voir ça se fait, mais c'est barbant, rasoir, suant, assommant, soulant, et tout ce que vous voulez d'autre et il y avait seulement 3 champs ! On va donc utiliser notre propre descripteur.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-7">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-7"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">GetField</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">num</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="nb">str</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot; num - numero de champs a extraire. type - type du champs. &quot;&quot;&quot;</span> <span class="bp">self</span><span class="o">.</span><span class="n">_num</span> <span class="o">=</span> <span class="n">num</span> <span class="bp">self</span><span class="o">.</span><span class="n">_type</span> <span class="o">=</span> <span class="nb">type</span> <span class="k">def</span> <span class="nf">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="bp">cls</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_type</span><span class="p">(</span><span class="n">obj</span><span class="o">.</span><span class="n">_fields</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">_num</span><span class="p">])</span> <span class="k">def</span> <span class="nf">__set__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">_type</span><span class="p">):</span> <span class="n">obj</span><span class="o">.</span><span class="n">_fields</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">_num</span><span class="p">]</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="k">else</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">%s</span><span class="s2"> expected&quot;</span> <span class="o">%</span> <span class="bp">self</span><span class="o">.</span><span class="n">_type</span><span class="p">)</span> </pre> </div> </div> </div> <p>Ça s'utilise comme ça. <img alt="smile" src="/theme/img/smiley/smiley-smile.gif" /></p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-8">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-8"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Personne</span><span class="p">:</span> <span class="n">prenom</span> <span class="o">=</span> <span class="n">GetField</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="n">nom</span> <span class="o">=</span> <span class="n">GetField</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="n">age</span> <span class="o">=</span> <span class="n">GetField</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="nb">int</span><span class="p">)</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">line</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_fields</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">'</span><span class="se">\t</span><span class="s1">'</span><span class="p">)</span> <span class="k">def</span> <span class="nf">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="s2">&quot;</span><span class="se">\t</span><span class="s2">&quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_fields</span><span class="p">)</span> </pre> </div> </div> </div> <p>C'est quand même plus sympa, et GetField est réutilisable. Que demander de plus ? Un exemple d'utilisation de la classe Personne peut être ? <img alt="wink" src="/theme/img/smiley/smiley-wink.gif" /></p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-9">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-9"> <div class="highlight"> <pre class="literal-block"> <span class="n">p</span> <span class="o">=</span> <span class="n">Personne</span><span class="p">(</span><span class="s2">&quot;Gaston</span><span class="se">\t</span><span class="s2">Lagaffe</span><span class="se">\t</span><span class="s2">18&quot;</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">prenom</span><span class="p">)</span> <span class="c1"># Gaston</span> <span class="nb">print</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">nom</span><span class="p">)</span> <span class="c1"># Lagaffe</span> <span class="nb">print</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">age</span><span class="p">)</span> <span class="c1"># 18</span> <span class="n">p</span><span class="o">.</span><span class="n">prenom</span> <span class="o">=</span> <span class="s2">&quot;Pépé&quot;</span> <span class="n">p</span><span class="o">.</span><span class="n">nom</span> <span class="o">=</span> <span class="s2">&quot;La Jactance&quot;</span> <span class="n">p</span><span class="o">.</span><span class="n">age</span> <span class="o">=</span> <span class="mi">80</span> <span class="nb">print</span><span class="p">(</span><span class="n">p</span><span class="p">)</span> <span class="c1"># &quot;Pépé\tLa Jactance\t80&quot;</span> </pre> </div> </div> </div> <p>Voila j'espère que ça vous a plus alors à vos IDE et bon code ! <img alt="smile" src="/theme/img/smiley/smiley-smile.gif" /></p> La redéfinition de méthode2014-07-09T10:20:00+02:002014-07-09T10:20:00+02:00Vincent Mailloltag:autourducode.com,2014-07-09:/les-bases-de-la-poo-en-python-redefinition-de-methode.html<p class="first last">Lorsque l'on hérite d'une classe il arrive souvent que l'on souhaite modifier une méthode, la redéfinition de méthode, c'est le fait de changer une méthode qui existe déjà dans la classe mère.</p> <p>Lorsque l'on hérite d'une classe il arrive souvent que l'on souhaite modifier une méthode, la redéfinition de méthode, c'est le fait de changer une méthode qui existe déjà dans la classe mère.</p> <p>Pour exemple, voici une classe qui distribue des boissons</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Distributeur</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">boissons</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_boissons</span> <span class="o">=</span> <span class="n">boissons</span> <span class="k">def</span> <span class="nf">a_boire</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="s2">&quot;return None s'il n'y a plus de boisson&quot;</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_boissons</span><span class="p">:</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_boissons</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="kc">None</span> </pre> </div> </div> </div> <p>ça s'utilise comme ça</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-2">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="n">clooney</span> <span class="o">=</span> <span class="n">Distributeur</span><span class="p">([</span><span class="s2">&quot;café&quot;</span><span class="p">,</span> <span class="s2">&quot;café&quot;</span><span class="p">])</span> <span class="nb">print</span><span class="p">(</span><span class="n">clooney</span><span class="o">.</span><span class="n">a_boire</span><span class="p">())</span> <span class="c1"># &quot;café&quot;</span> <span class="nb">print</span><span class="p">(</span><span class="n">clooney</span><span class="o">.</span><span class="n">a_boire</span><span class="p">())</span> <span class="c1"># &quot;café&quot;</span> <span class="nb">print</span><span class="p">(</span><span class="n">clooney</span><span class="o">.</span><span class="n">a_boire</span><span class="p">())</span> <span class="c1"># None</span> </pre> </div> </div> </div> <p>On voudrait afficher le nombre de café distribuer en ajoutant une méthode nb_boisson_vendu Voici la pire manière de faire.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-3">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">DistributeurCompteur</span><span class="p">(</span><span class="n">Distributeur</span><span class="p">):</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">boissons</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_boissons</span> <span class="o">=</span> <span class="n">boissons</span> <span class="bp">self</span><span class="o">.</span><span class="n">_nb_boisson</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">def</span> <span class="nf">a_boire</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_boissons</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">_nb_boisson</span> <span class="o">-=</span> <span class="mi">1</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_boissons</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span> <span class="k">return</span> <span class="kc">None</span> <span class="k">def</span> <span class="nf">nb_boisson_vendu</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_nb_boisson</span> </pre> </div> </div> </div> <p>En fait en fessant cela, on a réécrit l'intégralité de la classe. Cela pose deux problèmes le premier et que c'est une perte de temps le deuxième et que si du code marche, il n'y a aucune raison d'y toucher.</p> <div class="section" id="la-fonction-super"> <h2>La fonction super</h2> <p>Lorsque l'on redéfinie une méthode, on va appeler la méthode de la classe mère dans la méthode de la classe fille en utilisant la fonction super. En python3 super s'utilise sans paramètre en python2 il faut donner la classe parent et self.</p> <p>Voici la bonne façon de faire. <img alt="smile" src="/theme/img/smiley/smiley-smile.gif" /></p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-4">Python 3 </a></li> <li><a class="reference internal" href="#code-5">Python 2 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">DistributeurCompteur</span><span class="p">(</span><span class="n">Distributeur</span><span class="p">):</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">boissons</span><span class="p">):</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">boissons</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">_nb_boisson</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">def</span> <span class="nf">a_boire</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">boisson</span> <span class="o">=</span> <span class="nb">super</span><span class="p">(</span><span class="n">DistributeurCompteur</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">a_boire</span><span class="p">(</span><span class="n">boissons</span><span class="p">)</span> <span class="k">if</span> <span class="n">boisson</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">_nb_boisson</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">return</span> <span class="kc">None</span> <span class="k">def</span> <span class="nf">nb_boisson_vendu</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_nb_boisson</span> </pre> </div> </div> <div class="tab-pane" id="code-5"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">DistributeurCompteur</span><span class="p">(</span><span class="n">Distributeur</span><span class="p">):</span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">boissons</span><span class="p">):</span> <span class="nb">super</span><span class="p">(</span><span class="n">DistributeurCompteur</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">boissons</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">_nb_boisson</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">def</span> <span class="nf">a_boire</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">boisson</span> <span class="o">=</span> <span class="nb">super</span><span class="p">(</span><span class="n">DistributeurCompteur</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">a_boire</span><span class="p">(</span><span class="n">boissons</span><span class="p">)</span> <span class="k">if</span> <span class="n">boisson</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">_nb_boisson</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">return</span> <span class="bp">None</span> <span class="k">def</span> <span class="nf">nb_boisson_vendu</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_nb_boisson</span> </pre> </div> </div> </div> </div> <div class="section" id="le-prefix-double-underscore"> <h2>Le préfix double underscore __</h2> <p>Lorsque vous écrivez une classe il se peut que vous tendiez des pièges aux gens qui souhaiteraient hériter de vos classes. <img alt="innocent" src="/theme/img/smiley/smiley-innocent.gif" /> Vous avez écrit la classe trajet pour calculer le temps d'un trajet et sa distance</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-6">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-6"> <div class="highlight"> <pre class="literal-block"> <span class="kn">from</span> <span class="nn">math</span> <span class="k">import</span> <span class="n">sqrt</span> <span class="k">class</span> <span class="nc">Trajet</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">p1</span><span class="p">,</span> <span class="n">p2</span><span class="p">,</span> <span class="n">vitesse</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot; p1 - Point de départ (x, y) p2 - Point d'arrivé (x, y) vitesse - nombre représentant la vitesse de déplacement. &quot;&quot;&quot;</span> <span class="bp">self</span><span class="o">.</span><span class="n">p1</span> <span class="o">=</span> <span class="n">p1</span> <span class="bp">self</span><span class="o">.</span><span class="n">p2</span> <span class="o">=</span> <span class="n">p2</span> <span class="bp">self</span><span class="o">.</span><span class="n">vitesse</span> <span class="o">=</span> <span class="n">vitesse</span> <span class="k">def</span> <span class="nf">distance</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot; Retourne la distance du trajet. &quot;&quot;&quot;</span> <span class="n">x1</span><span class="p">,</span> <span class="n">y1</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">p1</span> <span class="n">x2</span><span class="p">,</span> <span class="n">y2</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">p2</span> <span class="k">return</span> <span class="n">sqrt</span><span class="p">(</span><span class="nb">abs</span><span class="p">(</span><span class="n">x1</span> <span class="o">-</span> <span class="n">x2</span><span class="p">)</span> <span class="o">**</span> <span class="mi">2</span> <span class="o">+</span> <span class="nb">abs</span><span class="p">(</span><span class="n">y1</span> <span class="o">-</span> <span class="n">y2</span><span class="p">)</span> <span class="o">**</span> <span class="mi">2</span><span class="p">)</span> <span class="k">def</span> <span class="nf">temps</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot; Retourne le temps du trajet. &quot;&quot;&quot;</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">distance</span><span class="p">()</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">vitesse</span> </pre> </div> </div> </div> <p>George a créé un site de co-voiturage et souhaite utiliser votre classe, mais dans son programme le méthode distance doit retourner une chaîne de caractère. Pour adapter votre code il hérite de votre classe et redéfinie la méthodes.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-7">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-7"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">TrajetMax</span><span class="p">(</span><span class="n">Trajet</span><span class="p">):</span> <span class="k">def</span> <span class="nf">distance</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="s2">&quot;9O km&quot;</span> <span class="nb">print</span><span class="p">(</span><span class="n">TrajetMax</span><span class="p">((</span><span class="mi">10</span><span class="p">,</span> <span class="mi">3</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">),</span> <span class="mi">3</span><span class="p">)</span><span class="o">.</span><span class="n">distance</span><span class="p">())</span> <span class="c1"># &quot;9O km&quot;</span> <span class="nb">print</span><span class="p">(</span><span class="n">TrajetMax</span><span class="p">((</span><span class="mi">10</span><span class="p">,</span> <span class="mi">3</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">),</span> <span class="mi">3</span><span class="p">)</span><span class="o">.</span><span class="n">temps</span><span class="p">())</span> <span class="c1"># &quot;9O km9O km9O km&quot;</span> </pre> </div> </div> </div> <p>Mais voilà, la méthode temps ne marche plus. Le problème, c'est que maintenant, trajet utilise la méthode redéfinie pour calculer le temps. Une solution est de protéger les noms de méthode appelées par d'autre en les préfixant avec un double underscore __</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-8">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-8"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Trajet</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">p1</span><span class="p">,</span> <span class="n">p2</span><span class="p">,</span> <span class="n">vitesse</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">p1</span> <span class="o">=</span> <span class="n">p1</span> <span class="bp">self</span><span class="o">.</span><span class="n">p2</span> <span class="o">=</span> <span class="n">p2</span> <span class="bp">self</span><span class="o">.</span><span class="n">vitesse</span> <span class="o">=</span> <span class="n">vitesse</span> <span class="k">def</span> <span class="nf">__distance</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">x1</span><span class="p">,</span> <span class="n">y1</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">p1</span> <span class="n">x2</span><span class="p">,</span> <span class="n">y2</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">p2</span> <span class="k">return</span> <span class="n">sqrt</span><span class="p">(</span><span class="nb">abs</span><span class="p">(</span><span class="n">x1</span> <span class="o">-</span> <span class="n">x2</span><span class="p">)</span> <span class="o">**</span> <span class="mi">2</span> <span class="o">+</span> <span class="nb">abs</span><span class="p">(</span><span class="n">y1</span> <span class="o">-</span> <span class="n">y2</span><span class="p">)</span> <span class="o">**</span> <span class="mi">2</span><span class="p">)</span> <span class="k">def</span> <span class="nf">temps</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">__distance</span><span class="p">()</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">vitesse</span> <span class="k">class</span> <span class="nc">TrajetMax</span><span class="p">(</span><span class="n">Trajet</span><span class="p">):</span> <span class="k">def</span> <span class="nf">distance</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="s2">&quot;</span><span class="si">{}</span><span class="s2"> km&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">super</span><span class="p">(</span><span class="n">TrajetMax</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">distance</span><span class="p">())</span> <span class="nb">print</span><span class="p">(</span><span class="n">TrajetMax</span><span class="p">((</span><span class="mi">10</span><span class="p">,</span> <span class="mi">3</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">),</span> <span class="mi">3</span><span class="p">)</span><span class="o">.</span><span class="n">temps</span><span class="p">())</span> <span class="c1"># 27.1661554144</span> </pre> </div> </div> </div> <p>Super ! Mais comment ça marche ?</p> <p>C'est ce que l'on appelle le <strong>substantypage</strong>. En python quand un attribut d'une classe est prefixée par __ il se transforme en _Nomdeclass__attr</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-9">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-9"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Foo</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__boo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="nb">print</span> <span class="s2">&quot;ok&quot;</span> <span class="n">Foo</span><span class="p">()</span><span class="o">.</span><span class="n">__boo</span><span class="p">()</span> <span class="c1"># &quot;AttributeError: 'Foo' object has no attribute '__boo'</span> <span class="n">Foo</span><span class="p">()</span><span class="o">.</span><span class="n">_Foo__boo</span><span class="p">()</span> <span class="c1"># ok</span> </pre> </div> </div> </div> <p>Par contre, si l'on fait comme ça, un autre problème se pose, on n'a plus de méthode publique dans Trajet pour avoir la distance. La solution est de garder notre méthode publique est de créer un alias privé.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-10">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-10"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Trajet</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">p1</span><span class="p">,</span> <span class="n">p2</span><span class="p">,</span> <span class="n">vitesse</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">p1</span> <span class="o">=</span> <span class="n">p1</span> <span class="bp">self</span><span class="o">.</span><span class="n">p2</span> <span class="o">=</span> <span class="n">p2</span> <span class="bp">self</span><span class="o">.</span><span class="n">vitesse</span> <span class="o">=</span> <span class="n">vitesse</span> <span class="k">def</span> <span class="nf">distance</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">x1</span><span class="p">,</span> <span class="n">y1</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">p1</span> <span class="n">x2</span><span class="p">,</span> <span class="n">y2</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">p2</span> <span class="k">return</span> <span class="n">sqrt</span><span class="p">(</span><span class="nb">abs</span><span class="p">(</span><span class="n">x1</span> <span class="o">-</span> <span class="n">x2</span><span class="p">)</span> <span class="o">**</span> <span class="mi">2</span> <span class="o">+</span> <span class="nb">abs</span><span class="p">(</span><span class="n">y1</span> <span class="o">-</span> <span class="n">y2</span><span class="p">)</span> <span class="o">**</span> <span class="mi">2</span><span class="p">)</span> <span class="n">__distance</span> <span class="o">=</span> <span class="n">distance</span> <span class="c1"># Creer un alias privé de distance.</span> <span class="k">def</span> <span class="nf">temps</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">__distance</span><span class="p">()</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">vitesse</span> </pre> </div> </div> </div> <p>Pour conclure, ce troisième chapitre sur les bases de la POO lorsque vous redéfinissez des méthodes, vous devez utiliser super pour appeler les méthodes de la classe mère. Dans votre classe si une méthode est utilisée par d'autre méthode: Elle doit être préfixée par __ si elle est privée. Si elle doit être publique, créez un alias __ma_methode = ma_methode et utilisé l'alias dans les autres méthodes.</p> <p>Vous avez les bases de la POO en python, donc à vos IDE et bon code! <img alt="smile" src="/theme/img/smiley/smiley-smile.gif" /></p> </div> Python et les metaclasses2014-07-06T17:38:00+02:002014-07-06T17:38:00+02:00Vincent Mailloltag:autourducode.com,2014-07-06:/python-et-les-metaclasses.html<p class="first last">En python tout est objet et même une classe est objet, mais si une classe est un objet comment est-elle instanciée ?</p> <p>En python tout est objet et même une classe est objet, mais si une classe est un objet comment est-elle instanciée ?</p> <p>Vous l'avez compris, les classes sont les instances des métaclasses.</p> <p>Commençons par un petit jeu, si je veux savoir qui a instancié un objet je peux appeler type sur mon objet voyons ce que cela donne avec les classes.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">Python 3 </a></li> <li><a class="reference internal" href="#code-2">Python </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="k">class</span> <span class="nc">Foo</span><span class="p">:</span> <span class="o">...</span> <span class="o">...</span> <span class="o">...</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">foo</span> <span class="o">=</span> <span class="n">Foo</span><span class="p">()</span> <span class="o">&gt;&gt;&gt;</span> <span class="nb">type</span><span class="p">(</span><span class="n">foo</span><span class="p">)</span> <span class="o">&lt;</span><span class="k">class</span> <span class="err">'</span><span class="nc">__main__</span><span class="o">.</span><span class="n">Foo</span><span class="s1">'&gt;</span> <span class="o">&gt;&gt;&gt;</span> <span class="nb">type</span><span class="p">(</span><span class="n">Foo</span><span class="p">)</span> <span class="o">&lt;</span><span class="k">class</span> <span class="err">'</span><span class="nc">type</span><span class="s1">'&gt;</span> </pre> </div> </div> <div class="tab-pane" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="k">class</span> <span class="nc">Foo</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="o">...</span> <span class="k">pass</span> <span class="o">...</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">foo</span> <span class="o">=</span> <span class="n">Foo</span><span class="p">()</span> <span class="o">&gt;&gt;&gt;</span> <span class="nb">type</span><span class="p">(</span><span class="n">foo</span><span class="p">)</span> <span class="o">&lt;</span><span class="k">class</span> <span class="err">'</span><span class="nc">__main__</span><span class="o">.</span><span class="n">Foo</span><span class="s1">'&gt;</span> <span class="o">&gt;&gt;&gt;</span> <span class="nb">type</span><span class="p">(</span><span class="n">Foo</span><span class="p">)</span> <span class="o">&lt;</span><span class="nb">type</span> <span class="s1">'type'</span><span class="o">&gt;</span> </pre> </div> </div> </div> <p>Par défaut, la métaclasse c'est type !</p> <p>Ca veux dire que je peux créer des classes avec type !? <img alt="surprised" src="/theme/img/smiley/smiley-surprised.gif" /></p> <p>Parfaitement <img alt="smile" src="/theme/img/smiley/smiley-smile.gif" /> . En python, type sert à deux choses connaître le type d'un objet et créer dynamiquement de nouvelles classes. Pour ça, il faut donner 3 arguments:</p> <ol class="arabic simple"> <li>Le nom de la classe</li> <li>Un tuple contenant ses parents</li> <li>Ses attributs dans un dictionnaire</li> </ol> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-3">Python 3 </a></li> <li><a class="reference internal" href="#code-4">Python 2 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="n">Foo</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="s2">&quot;Foo&quot;</span><span class="p">,</span> <span class="p">(</span><span class="nb">object</span><span class="p">,),</span> <span class="p">{</span><span class="s2">&quot;a&quot;</span><span class="p">:</span> <span class="mi">45</span><span class="p">}</span> <span class="p">)</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">foo</span> <span class="o">=</span> <span class="n">Foo</span><span class="p">()</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">foo</span><span class="o">.</span><span class="n">a</span> <span class="mi">45</span> </pre> </div> </div> <div class="tab-pane" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="n">Foo</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="s2">&quot;Foo&quot;</span><span class="p">,</span> <span class="p">(</span><span class="nb">object</span><span class="p">,),</span> <span class="p">{</span><span class="s2">&quot;a&quot;</span><span class="p">:</span> <span class="mi">45</span><span class="p">}</span> <span class="p">)</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">foo</span> <span class="o">=</span> <span class="n">Foo</span><span class="p">()</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">foo</span><span class="o">.</span><span class="n">a</span> <span class="mi">45</span> </pre> </div> </div> </div> <p>Les méthodes étant des attributs de classe et non d'instance, on peut donc passer des méthodes au 3éme arguments de type.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-5">Python 3 </a></li> <li><a class="reference internal" href="#code-6">Python 2 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-5"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">a</span><span class="p">):</span> <span class="o">...</span> <span class="bp">self</span><span class="o">.</span><span class="n">a</span> <span class="o">=</span> <span class="n">a</span> <span class="o">...</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">Foo</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="s1">'Foo'</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">(),</span> <span class="p">{</span><span class="s1">'__init__'</span><span class="p">:</span> <span class="fm">__init__</span><span class="p">})</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">foo</span> <span class="o">=</span> <span class="n">Foo</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">foo</span><span class="o">.</span><span class="n">a</span> <span class="mi">42</span> </pre> </div> </div> <div class="tab-pane" id="code-6"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">a</span><span class="p">):</span> <span class="o">...</span> <span class="bp">self</span><span class="o">.</span><span class="n">a</span> <span class="o">=</span> <span class="n">a</span> <span class="o">...</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">Foo</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="s1">'Foo'</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">(),</span> <span class="p">{</span><span class="s1">'__init__'</span><span class="p">:</span> <span class="fm">__init__</span><span class="p">})</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">foo</span> <span class="o">=</span> <span class="n">Foo</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">foo</span><span class="o">.</span><span class="n">a</span> <span class="mi">42</span> </pre> </div> </div> </div> <div class="section" id="creer-c-est-propre-metaclasses"> <h2>Créer c'est propre métaclasses</h2> <p>Plus fort encore, on peut créer ses propres métaclasse. Généralement lorsque l'on souhaite modifier le comportement d'une classe on dérive de celle-ci. Il en est de même avec les métaclasses, on va donc dériver de type pour créer notre propre métaclasse qui affichera des informations sur la création de notre classe.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-7">Python 3 </a></li> <li><a class="reference internal" href="#code-8">Python 2 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-7"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">MetaClasse</span><span class="p">(</span><span class="nb">type</span><span class="p">):</span> <span class="k">def</span> <span class="nf">__new__</span><span class="p">(</span><span class="n">mcs</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">attrs</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;class '</span><span class="si">{}</span><span class="s2">' created&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">name</span><span class="p">))</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;class bases: </span><span class="si">{}</span><span class="s2">&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">bases</span><span class="p">))</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;class attributs: </span><span class="si">{}</span><span class="s2">&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">attrs</span><span class="p">))</span> <span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__new__</span><span class="p">(</span><span class="n">mcs</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">attrs</span><span class="p">)</span> </pre> </div> </div> <div class="tab-pane" id="code-8"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">MetaClasse</span><span class="p">(</span><span class="nb">type</span><span class="p">):</span> <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="n">mcs</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">attrs</span><span class="p">):</span> <span class="k">print</span> <span class="s2">&quot;class '{}' created&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">name</span><span class="p">)</span> <span class="k">print</span> <span class="s2">&quot;class bases: {}&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">bases</span><span class="p">)</span> <span class="k">print</span> <span class="s2">&quot;class attributs: {}&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">attrs</span><span class="p">)</span> <span class="k">return</span> <span class="nb">super</span><span class="p">(</span><span class="n">MetaClasse</span><span class="p">,</span> <span class="n">mcs</span><span class="p">)</span><span class="o">.</span><span class="fm">__new__</span><span class="p">(</span><span class="n">mcs</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">attrs</span><span class="p">)</span> </pre> </div> </div> </div> <p>Comme vous pouvez le voir j'ai utilisé __new__ et non __init__. Lors d'un appel à __new__, ma classe qui sera instanciée par MetaClasse n'existe pas encore et le premier paramètre de __new__ est MetaClasse. Si vous utilisez la méthode __init__, le premier paramètre sera par contre votre classe.</p> <p>Habituellement on ne crée pas une classe dynamiquement, on la déclare. Cela ne nous empêche pas de la rattacher à une métaclasse en effet, la déclaration de classe aussi statique soit elle fera quand même travailler type. Pour faire travailler notre métaclasse à la place de type dans une déclaration statique on fait comme ça:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-9">Python 3 </a></li> <li><a class="reference internal" href="#code-10">Python 2 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-9"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="k">class</span> <span class="nc">Foo</span><span class="p">(</span><span class="n">metaclass</span><span class="o">=</span><span class="n">MetaClasse</span><span class="p">):</span> <span class="o">...</span> <span class="o">...</span> <span class="o">...</span> <span class="k">class</span> <span class="err">'</span><span class="nc">Foo</span><span class="s1">' created</span> <span class="k">class</span> <span class="nc">bases</span><span class="p">:</span> <span class="p">()</span> <span class="k">class</span> <span class="nc">attributs</span><span class="p">:</span> <span class="p">{</span><span class="s1">'__module__'</span><span class="p">:</span> <span class="s1">'__main__'</span><span class="p">,</span> <span class="s1">'__qualname__'</span><span class="p">:</span> <span class="s1">'Foo'</span><span class="p">}</span> </pre> </div> </div> <div class="tab-pane" id="code-10"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="k">class</span> <span class="nc">Foo</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="o">...</span> <span class="vm">__metaclass__</span> <span class="o">=</span> <span class="n">MetaClasse</span> <span class="o">...</span> <span class="k">class</span> <span class="err">'</span><span class="nc">Foo</span><span class="s1">' created</span> <span class="k">class</span> <span class="nc">bases</span><span class="p">:</span> <span class="p">(</span><span class="o">&lt;</span><span class="nb">type</span> <span class="s1">'object'</span><span class="o">&gt;</span><span class="p">,)</span> <span class="k">class</span> <span class="nc">attributs</span><span class="p">:</span> <span class="p">{</span><span class="s1">'__module__'</span><span class="p">:</span> <span class="s1">'__main__'</span><span class="p">,</span> <span class="s1">'__metaclass__'</span><span class="p">:</span> <span class="o">&lt;</span><span class="k">class</span> <span class="err">'</span><span class="nc">__main__</span><span class="o">.</span><span class="n">metaclasse</span><span class="s1">'&gt;}</span> </pre> </div> </div> </div> </div> <div class="section" id="a-quoi-ca-sert"> <h2>A quoi ça sert ?</h2> <p>Vous concevez un framework et vous souhaitez que certaine des classes aient une méthode run. Vous pouvez utiliser les métaclasse pour ça.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-11">Python 3 </a></li> <li><a class="reference internal" href="#code-12">Python 2 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-11"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">MetaFilter</span><span class="p">(</span><span class="nb">type</span><span class="p">):</span> <span class="k">def</span> <span class="nf">__new__</span><span class="p">(</span><span class="n">mcs</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">attrs</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot; Test lors de la fabrication de la classe s'il y a bien une methode run. &quot;&quot;&quot;</span> <span class="k">if</span> <span class="s1">'run'</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">attrs</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">(</span><span class="s2">&quot;You must redefine 'run' methode in class </span><span class="si">{}</span><span class="s2">&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">name</span><span class="p">))</span> <span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__new__</span><span class="p">(</span><span class="n">mcs</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">attrs</span><span class="p">)</span> </pre> </div> </div> <div class="tab-pane" id="code-12"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">MetaFilter</span><span class="p">(</span><span class="nb">type</span><span class="p">):</span> <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="n">mcs</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">attrs</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot; Test lors de la fabrication de la classe s'il y a bien une methode run. &quot;&quot;&quot;</span> <span class="k">if</span> <span class="s1">'run'</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">attrs</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">(</span><span class="s2">&quot;You must redefine 'run' methode in class {}&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">name</span><span class="p">))</span> <span class="k">return</span> <span class="nb">super</span><span class="p">(</span><span class="n">MetaFilter</span><span class="p">,</span> <span class="n">mcs</span><span class="p">)</span><span class="o">.</span><span class="fm">__new__</span><span class="p">(</span><span class="n">mcs</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">attrs</span><span class="p">)</span> </pre> </div> </div> </div> <p>Une classe fille a pour métaclasse la même classe que sa classe mère. On va utiliser ce fait pour créer une classe de base qui sera redéfinie par l'utilisateur final.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-13">Python 3 </a></li> <li><a class="reference internal" href="#code-14">Python 2 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-13"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">BaseFilter</span><span class="p">(</span><span class="n">metaclass</span><span class="o">=</span><span class="n">MetaFilter</span><span class="p">):</span> <span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">(</span><span class="s2">&quot;You must redefine 'run' methode&quot;</span><span class="p">)</span> </pre> </div> </div> <div class="tab-pane" id="code-14"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">BaseFilter</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="vm">__metaclass__</span> <span class="o">=</span> <span class="n">MetaFilter</span> <span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">(</span><span class="s2">&quot;You must redefine 'run' methode&quot;</span><span class="p">)</span> </pre> </div> </div> </div> <p>Le gros avantage est que l'erreur est indiquée très tôt et qu'une seule fois au démarrage de l'application.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-15">Python 3 </a></li> <li><a class="reference internal" href="#code-16">Python 2 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-15"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="k">class</span> <span class="nc">MyFilter</span><span class="p">(</span><span class="n">BaseFilter</span><span class="p">):</span> <span class="o">...</span> <span class="o">...</span> <span class="o">...</span> <span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> <span class="n">File</span> <span class="s2">&quot;&lt;stdin&gt;&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span> <span class="n">File</span> <span class="s2">&quot;&lt;stdin&gt;&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">7</span><span class="p">,</span> <span class="ow">in</span> <span class="fm">__new__</span> <span class="ne">NotImplementedError</span><span class="p">:</span> <span class="n">You</span> <span class="n">must</span> <span class="n">redefine</span> <span class="s1">'run'</span> <span class="n">methode</span> <span class="ow">in</span> <span class="k">class</span> <span class="nc">MyFilter</span> </pre> </div> </div> <div class="tab-pane" id="code-16"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="k">class</span> <span class="nc">MyFilter</span><span class="p">(</span><span class="n">BaseFilter</span><span class="p">):</span> <span class="o">...</span> <span class="k">pass</span> <span class="o">...</span> <span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> <span class="n">File</span> <span class="s2">&quot;&lt;stdin&gt;&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span> <span class="n">File</span> <span class="s2">&quot;&lt;stdin&gt;&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">7</span><span class="p">,</span> <span class="ow">in</span> <span class="fm">__new__</span> <span class="ne">NotImplementedError</span><span class="p">:</span> <span class="n">You</span> <span class="n">must</span> <span class="n">redefine</span> <span class="s1">'run'</span> <span class="n">methode</span> <span class="ow">in</span> <span class="k">class</span> <span class="nc">MyFilter</span> </pre> </div> </div> </div> <p>Un autre cas intéressant est de faire un registre pour l'injection de dépendance qui ajoute automatiquement une classes à sa déclaration.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-17">Python 3 </a></li> <li><a class="reference internal" href="#code-18">Python 2 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-17"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">MetaRegistry</span><span class="p">(</span><span class="nb">type</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot; Registre capturant les classes &quot;&quot;&quot;</span> <span class="n">__current_class_filter</span> <span class="o">=</span> <span class="p">{}</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">attrs</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot; Les classes filles remplacent les classe de base dans le registre. Les classes de bases sont les classes par defaut. Si un utilisateur hérite d'une classe de bases, ça classe remplacera la classe de base dans le registre. &quot;&quot;&quot;</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">bases</span> <span class="ow">or</span> <span class="nb">object</span> <span class="ow">in</span> <span class="n">bases</span><span class="p">:</span> <span class="n">MetaRegistry</span><span class="o">.</span><span class="n">__current_class_filter</span><span class="p">[</span><span class="bp">cls</span><span class="p">]</span> <span class="o">=</span> <span class="bp">cls</span> <span class="k">else</span><span class="p">:</span> <span class="n">MetaRegistry</span><span class="o">.</span><span class="n">__current_class_filter</span><span class="p">[</span><span class="n">bases</span><span class="p">[</span><span class="mi">0</span><span class="p">]]</span> <span class="o">=</span> <span class="bp">cls</span> <span class="nd">&#64;classmethod</span> <span class="k">def</span> <span class="nf">get_class</span><span class="p">(</span><span class="n">mcs</span><span class="p">,</span> <span class="n">class_name</span><span class="p">):</span> <span class="k">return</span> <span class="n">mcs</span><span class="o">.</span><span class="n">__current_class_filter</span><span class="p">[</span><span class="n">class_name</span><span class="p">]</span> <span class="k">class</span> <span class="nc">BaseInFilter</span><span class="p">(</span><span class="n">metaclass</span><span class="o">=</span><span class="n">MetaRegistry</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot; Classe de base utilisé par défaut par le framework. filtre les données en entrée. Ne fait rien par défaut. &quot;&quot;&quot;</span> <span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="k">return</span> <span class="n">value</span> <span class="k">class</span> <span class="nc">BaseToPy</span><span class="p">(</span><span class="n">metaclass</span><span class="o">=</span><span class="n">MetaRegistry</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot; Classe de base utilisé par défaut par le framework. convertie une valeur en objet python str -&gt; str par défaut &quot;&quot;&quot;</span> <span class="k">def</span> <span class="nf">cast</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="k">return</span> <span class="n">value</span> <span class="k">def</span> <span class="nf">valide_input</span><span class="p">(</span><span class="n">value</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot; Fonction interne au framework utilisant l'injection de dépendance. en appelant le MetaRegistry. &quot;&quot;&quot;</span> <span class="n">to_py</span> <span class="o">=</span> <span class="n">MetaRegistry</span><span class="o">.</span><span class="n">get_class</span><span class="p">(</span><span class="n">BaseToPy</span><span class="p">)()</span> <span class="n">in_filter</span> <span class="o">=</span> <span class="n">MetaRegistry</span><span class="o">.</span><span class="n">get_class</span><span class="p">(</span><span class="n">BaseInFilter</span><span class="p">)()</span> <span class="k">return</span> <span class="n">to_py</span><span class="o">.</span><span class="n">cast</span><span class="p">(</span><span class="n">in_filter</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">value</span><span class="p">))</span> <span class="c1"># L'utilisateur déclare cette classe car le comportement de BaseInFilter ne lui convient pas.</span> <span class="c1"># Elle va aller remplacer BaseInFilter dans le registre</span> <span class="k">class</span> <span class="nc">MyInFilter</span><span class="p">(</span><span class="n">BaseInFilter</span><span class="p">):</span> <span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="k">return</span> <span class="n">value</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s1">','</span><span class="p">,</span> <span class="s1">'.'</span><span class="p">)</span> </pre> </div> </div> <div class="tab-pane" id="code-18"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">MetaRegistry</span><span class="p">(</span><span class="nb">type</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot; Registre capturant les classes &quot;&quot;&quot;</span> <span class="n">__current_class_filter</span> <span class="o">=</span> <span class="p">{}</span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">attrs</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot; Les classes filles remplacent les classe de base dans le registre. Les classes de bases sont les classes par defaut. Si un utilisateur hérite d'une classe de bases, ça classe remplacera la classe de base dans le registre. &quot;&quot;&quot;</span> <span class="k">if</span> <span class="nb">object</span> <span class="ow">in</span> <span class="n">bases</span><span class="p">:</span> <span class="n">MetaRegistry</span><span class="o">.</span><span class="n">__current_class_filter</span><span class="p">[</span><span class="bp">cls</span><span class="p">]</span> <span class="o">=</span> <span class="bp">cls</span> <span class="k">else</span><span class="p">:</span> <span class="n">MetaRegistry</span><span class="o">.</span><span class="n">__current_class_filter</span><span class="p">[</span><span class="n">bases</span><span class="p">[</span><span class="mi">0</span><span class="p">]]</span> <span class="o">=</span> <span class="bp">cls</span> <span class="nd">&#64;classmethod</span> <span class="k">def</span> <span class="nf">get_class</span><span class="p">(</span><span class="n">mcs</span><span class="p">,</span> <span class="n">class_name</span><span class="p">):</span> <span class="k">return</span> <span class="n">mcs</span><span class="o">.</span><span class="n">__current_class_filter</span><span class="p">[</span><span class="n">class_name</span><span class="p">]</span> <span class="k">class</span> <span class="nc">BaseInFilter</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot; Classe de base utilisé par défaut par le framework. filtre les données en entrée. Ne fait rien par défaut. &quot;&quot;&quot;</span> <span class="vm">__metaclass__</span> <span class="o">=</span> <span class="n">MetaRegistry</span> <span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="k">return</span> <span class="n">value</span> <span class="k">class</span> <span class="nc">BaseToPy</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot; Classe de base utilisé par défaut par le framework. convertie une valeur en objet python str -&gt; str par défaut &quot;&quot;&quot;</span> <span class="vm">__metaclass__</span> <span class="o">=</span> <span class="n">MetaRegistry</span> <span class="k">def</span> <span class="nf">cast</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="k">return</span> <span class="n">value</span> <span class="k">def</span> <span class="nf">valide_input</span><span class="p">(</span><span class="n">value</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot; Fonction interne au framework utilisant l'injection de dépendance. &quot;&quot;&quot;</span> <span class="n">to_py</span> <span class="o">=</span> <span class="n">MetaRegistry</span><span class="o">.</span><span class="n">get_class</span><span class="p">(</span><span class="n">BaseToPy</span><span class="p">)()</span> <span class="n">in_filter</span> <span class="o">=</span> <span class="n">MetaRegistry</span><span class="o">.</span><span class="n">get_class</span><span class="p">(</span><span class="n">BaseInFilter</span><span class="p">)()</span> <span class="k">return</span> <span class="n">to_py</span><span class="o">.</span><span class="n">cast</span><span class="p">(</span><span class="n">in_filter</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">value</span><span class="p">))</span> <span class="c1"># L'utilisateur déclare cette classe car le comportement de BaseInFilter ne lui convient pas.</span> <span class="c1"># Elle va aller remplacer BaseInFilter dans le registre</span> <span class="k">class</span> <span class="nc">MyInFilter</span><span class="p">(</span><span class="n">BaseInFilter</span><span class="p">):</span> <span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="k">return</span> <span class="n">value</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s1">','</span><span class="p">,</span> <span class="s1">'.'</span><span class="p">)</span> <span class="k">print</span> <span class="n">valide_input</span><span class="p">(</span><span class="s2">&quot;1,66&quot;</span><span class="p">)</span> </pre> </div> </div> </div> <p>Voila, vous en savez plus sur les metaclasses. Donc à vos IDE et bon code ! <img alt="laughing" src="/theme/img/smiley/smiley-laughing.gif" /></p> </div> Les bases de la POO en Python l'héritage2014-07-04T08:26:00+02:002014-07-04T08:26:00+02:00Vincent Mailloltag:autourducode.com,2014-07-04:/les-bases-de-la-poo-en-python-heritage.html<p class="first last">L'héritage permet de créer une classe à partir d'une autre que l'on appelle classe de base ou classe mère. La classe ainsi créée possédera tous les attributs et méthode de la classe mère.</p> <div class="section" id="l-heritage-simple"> <h2>L'héritage simple</h2> <p>L'héritage permet de créer une classe à partir d'une autre que l'on appelle <strong>classe de base</strong> ou <strong>classe mère</strong>. La classe ainsi créée possédera tous les attributs et méthode de la classe mère.</p> <p>Pour illustrer ça, nous allons faire une classe Counter. Jusque la rien de nouveau</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Counter</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_i</span> <span class="o">=</span> <span class="n">i</span> <span class="k">def</span> <span class="nf">incremente</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_i</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">def</span> <span class="nf">value</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_i</span> </pre> </div> </div> </div> <p>Voici l'exemple</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-2">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="n">c</span> <span class="o">=</span> <span class="n">Counter</span><span class="p">()</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">c</span><span class="o">.</span><span class="n">incremente</span><span class="p">()</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">c</span><span class="o">.</span><span class="n">incremente</span><span class="p">()</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">c</span><span class="o">.</span><span class="n">value</span><span class="p">()</span> <span class="mi">2</span> </pre> </div> </div> </div> <p>Maintenant si l'on souhaite avoir une classe comme Counter, mais qui puisse aussi décompter, on va hériter de Counter</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-3">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">ReverseCounter</span><span class="p">(</span><span class="n">Counter</span><span class="p">):</span> <span class="c1"># On donne la classe mere ici.</span> <span class="k">def</span> <span class="nf">decremente</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_i</span> <span class="o">-=</span> <span class="mi">1</span> <span class="c1"># _i est un attribut hérité de Counter</span> </pre> </div> </div> </div> <p>Notre classe ReverseCounter peut faire ce que fait Counter car elle possède la méthode incremente</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-4">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="n">nb_mouton</span> <span class="o">=</span> <span class="n">ReverseCounter</span><span class="p">()</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">nb_mouton</span><span class="o">.</span><span class="n">incremente</span><span class="p">()</span> <span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span><span class="p">(</span><span class="n">nb_mouton</span><span class="o">.</span><span class="n">value</span><span class="p">())</span> <span class="mi">1</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">nb_mouton</span><span class="o">.</span><span class="n">decremente</span><span class="p">()</span> <span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span><span class="p">(</span><span class="n">nb_mouton</span><span class="o">.</span><span class="n">value</span><span class="p">())</span> <span class="mi">0</span> </pre> </div> </div> </div> <p>ReverseCounter a également hérité de la méthode __init__</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-5">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-5"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="n">nb_mouton</span> <span class="o">=</span> <span class="n">ReverseCounter</span><span class="p">(</span><span class="mi">33</span><span class="p">)</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">nb_mouton</span><span class="o">.</span><span class="n">decremente</span><span class="p">()</span> <span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span><span class="p">(</span><span class="n">nb_mouton</span><span class="o">.</span><span class="n">value</span><span class="p">())</span> <span class="mi">32</span> </pre> </div> </div> </div> <p>On dit que ReverseCounter hérite ou dérive de Counter avec l'héritage, on doit pouvoir utiliser un objet ReverseCounter là où l'on utilise un Counter. La fonction isinstance permet de savoir si un objet est l'instance d'une classe donnée ou d'une classe mère.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-6">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-6"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="n">rc</span> <span class="o">=</span> <span class="n">ReverseCounter</span><span class="p">()</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">c</span> <span class="o">=</span> <span class="n">Counter</span><span class="p">()</span> <span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span><span class="p">(</span><span class="nb">isinstance</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">Counter</span><span class="p">))</span> <span class="kc">True</span> <span class="o">&gt;&gt;&gt;</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">rc</span><span class="p">,</span> <span class="n">Counter</span><span class="p">)</span> <span class="c1"># ReverseCounter est un Counter</span> <span class="kc">True</span> <span class="o">&gt;&gt;&gt;</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">ReverseCounter</span><span class="p">)</span> <span class="c1"># L'inverse n'est pas vrais</span> <span class="kc">False</span> </pre> </div> </div> </div> <p>De ce fait, une fonction ou une méthode qui utilise un Counter peut se satisfaire d'un reverseCounter.</p> </div> <div class="section" id="l-heritage-multiple"> <h2>L'héritage multiple</h2> <p>Python supporte également l'héritage multiple. Cela veut dire qu'une classe peut hériter de plusieurs autres classes.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-7">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-7"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Enseignant</span><span class="p">:</span> <span class="k">def</span> <span class="nf">enseigne</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;enseigne&quot;</span><span class="p">)</span> <span class="k">def</span> <span class="nf">titre</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;M&quot;</span><span class="p">)</span> <span class="k">class</span> <span class="nc">Chercheur</span><span class="p">:</span> <span class="k">def</span> <span class="nf">cherche</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;cherche&quot;</span><span class="p">)</span> <span class="k">def</span> <span class="nf">titre</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Dr&quot;</span><span class="p">)</span> <span class="k">class</span> <span class="nc">EnseignantChercheur</span><span class="p">(</span><span class="n">Enseignant</span><span class="p">,</span> <span class="n">Chercheur</span><span class="p">):</span> <span class="k">pass</span> <span class="c1"># pass sinifie m'a class est terminé</span> </pre> </div> </div> </div> <p>Dans notre cas la classe EnseignantChercheur hérite de Enseigant et Chercher elle possède donc les méthodes enseigne et cherche.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-8">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-8"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="n">ec</span> <span class="o">=</span> <span class="n">EnseignantChercheur</span><span class="p">()</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">ec</span><span class="o">.</span><span class="n">cherche</span><span class="p">()</span> <span class="n">cherche</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">ec</span><span class="o">.</span><span class="n">enseigne</span><span class="p">()</span> <span class="n">enseigne</span> </pre> </div> </div> </div> <p>Mais que ce passe-t-il si les deux classes mères ont un même nom d'attribut ou méthode ? <img alt="undecided" src="/theme/img/smiley/smiley-undecided.gif" /></p> <p>C'est simple, c'est la classe déclarée en premier dans la classe fille qui gagne.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-9">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-9"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="n">ec</span> <span class="o">=</span> <span class="n">EnseignantChercheur</span><span class="p">()</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">ec</span><span class="o">.</span><span class="n">titre</span><span class="p">()</span> <span class="n">M</span> </pre> </div> </div> </div> <p>Voilà, j'espère que compter les moutons ne vous a pas endormi, le prochain chapitre sur la POO traitera de la redéfinition de méthode. n'hésité pas si vous avez des questions et bon code ! <img alt="smile" src="/theme/img/smiley/smiley-smile.gif" /></p> </div> Play le framework java inspiré de Django2014-06-30T12:35:00+02:002014-06-30T12:35:00+02:00Vincent Mailloltag:autourducode.com,2014-06-30:/play-le-framework-java-inspire-de-django.html<p class="first last">Aujourd'hui, j'ai voulu tester play pour me faire une idée et voir jusqu'où deux framework basés sur des technologies différentes peuvent ou pas se ressembler.</p> <p>Aujourd'hui, j'ai voulu tester play pour me faire une idée et voir jusqu'où deux framework basés sur des technologies différentes peuvent ou pas se ressembler.</p> <div class="section" id="prise-en-main"> <h2>Prise en main</h2> <p><strong>Play</strong> est aussi simple à prendre en main que <strong>Djando</strong>. À l'instar de Django, un script permet de créer ses applications.</p> <p><kbd class="kbd"> $ ./activator new my-app play-java</kbd> </p> <p>est l'équivalent de</p> <p><kbd class="kbd"> $ ./django-admin.py startapp my-app</kbd> </p> <p>Notez cependant qu'il existe une version graphique pour play.</p> <p>Une fois dans notre application, un script activator nous permet de lancer la console. La première fois c'est très long le temps de télécharger toutes les jars. La gestion des dépendances se fait avec le fichier <strong>build.sbt</strong>. C'est comme le fichier <strong>requirements.txt</strong> lorsque vous utilisez Pip sous Python. Tout comme Django, play intègre un serveur de développement, donc pas de prise de tête avec tomcat ou glassfish. Le site en dev est directement accessible sur le port 9000. Les fichiers modifiés sont rechargés à chaud et les erreurs de compilation ou de routage apparaissent dans votre navigateur.</p> <p>La console permet également de créer les fichier de configuration pour pouvoir utiliser votre IDE favori comme Eclipse ou autre pour développer votre application.</p> <p>On va enfin pouvoir entrer dans le vif du sujet. La première différence majeure entre les deux framework est l'architecture play c'est du MVC alors que Django du MVT.</p> </div> <div class="section" id="les-controleurs"> <h2>Les Controleurs</h2> <p>Les Controleurs sont des classes qui étendent play.mvc.Controller. Ils sont composés d'Action, ce sont des méthodes qui retourne un play.mvc.Result en utilisant request. C'est l'équivalent des views en Django. Le routage des actions est fait par le fichier texte /conf/routes qui par la suite est transformé en classe java. C'est l'équivalent de l'url.py.</p> <p>Exemple de controleur:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">Application.java</a></li> <li><a class="reference internal" href="#code-2">routes </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="kn">package</span> <span class="nn">controllers</span><span class="o">;</span> <span class="kn">import</span> <span class="nn">play.*</span><span class="o">;</span> <span class="kn">import</span> <span class="nn">play.mvc.*</span><span class="o">;</span> <span class="kn">import</span> <span class="nn">play.mvc.Http.*</span><span class="o">;</span> <span class="kn">import</span> <span class="nn">views.html.*</span><span class="o">;</span> <span class="kn">import</span> <span class="nn">models.*</span><span class="o">;</span> <span class="kn">import</span> <span class="nn">play.data.Form</span><span class="o">;</span> <span class="kn">import</span> <span class="nn">java.util.List</span><span class="o">;</span> <span class="kd">public</span> <span class="kd">class</span> <span class="nc">Application</span> <span class="kd">extends</span> <span class="n">Controller</span> <span class="o">{</span> <span class="cm">/** Action qui affiche la liste de mes clients */</span> <span class="kd">public</span> <span class="kd">static</span> <span class="n">Result</span> <span class="nf">list_user</span><span class="o">()</span> <span class="o">{</span> <span class="n">List</span><span class="o">&lt;</span><span class="n">Customer</span><span class="o">&gt;</span> <span class="n">customers</span> <span class="o">=</span> <span class="n">Customer</span><span class="o">.</span><span class="na">find</span><span class="o">.</span><span class="na">all</span><span class="o">();</span> <span class="c1">// Requête </span> <span class="k">return</span> <span class="n">ok</span><span class="o">(</span><span class="n">views</span><span class="o">.</span><span class="na">html</span><span class="o">.</span><span class="na">Application</span><span class="o">.</span><span class="na">list_user</span><span class="o">.</span><span class="na">render</span><span class="o">(</span><span class="n">customers</span><span class="o">));</span> <span class="o">}</span> <span class="o">...</span> <span class="n">d</span><span class="err">'</span><span class="n">autre</span> <span class="n">actions</span> <span class="o">}</span> </pre> </div> </div> <div class="tab-pane" id="code-2"> <div class="highlight"> <pre class="literal-block"> GET /list_user controllers.Application.list_user() POST /save_user controllers.Application.save_user() GET /add_user controllers.Application.add_user() </pre> </div> </div> </div> <p>La différence et qu'avec Django les vues gèrent elles-mêmes si elles s'occupent de requête POST, GET ou autres. Dans play c'est déclaré dans le fichier routes et une même action ne peut pas recevoir une route pour GET et une route pour POST.</p> </div> <div class="section" id="les-models"> <h2>Les Models</h2> <p>Coté ORM, c'est moins intégré que dans Django. Vous avez le choix entre <strong>Hibernate</strong> ou <strong>Ebean</strong>. Je me suis tourné vers Ebean. Contrairement à Hibernate, Ebean ne respecte pas la JPA (Java Persistance API) et utilise des annotations, il n'y a pas de fichier de mapping à écrire.</p> <p>Le modèle possède une méthode save et les requête peuvent être fait via l'objet Finder.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-3">Customer.java</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="kn">package</span> <span class="nn">models</span><span class="o">;</span> <span class="kn">import</span> <span class="nn">java.util.Date</span><span class="o">;</span> <span class="kn">import</span> <span class="nn">javax.persistence.*</span><span class="o">;</span> <span class="kn">import</span> <span class="nn">play.db.ebean.*</span><span class="o">;</span> <span class="kn">import</span> <span class="nn">play.data.format.*</span><span class="o">;</span> <span class="kn">import</span> <span class="nn">play.data.validation.Constraints</span><span class="o">;</span> <span class="nd">&#64;Entity</span> <span class="kd">public</span> <span class="kd">class</span> <span class="nc">Customer</span> <span class="kd">extends</span> <span class="n">Model</span> <span class="o">{</span> <span class="nd">&#64;Id</span> <span class="nd">&#64;GeneratedValue</span> <span class="kd">public</span> <span class="kt">long</span> <span class="n">id</span><span class="o">;</span> <span class="nd">&#64;Constraints.Required</span> <span class="kd">public</span> <span class="n">String</span> <span class="n">first_name</span><span class="o">;</span> <span class="nd">&#64;Constraints.Required</span> <span class="kd">public</span> <span class="n">String</span> <span class="n">last_name</span><span class="o">;</span> <span class="nd">&#64;Formats.DateTime</span><span class="o">(</span><span class="n">pattern</span><span class="o">=</span><span class="s">&quot;dd/MM/yyyy&quot;</span><span class="o">)</span> <span class="kd">public</span> <span class="n">Date</span> <span class="n">birthday_date</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Date</span><span class="o">();</span> <span class="kd">public</span> <span class="kd">static</span> <span class="n">Finder</span><span class="o">&lt;</span><span class="n">Long</span><span class="o">,</span><span class="n">Customer</span><span class="o">&gt;</span> <span class="n">find</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Finder</span><span class="o">&lt;</span><span class="n">Long</span><span class="o">,</span><span class="n">Customer</span><span class="o">&gt;(</span> <span class="n">Long</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="n">Customer</span><span class="o">.</span><span class="na">class</span> <span class="o">);</span> <span class="o">}</span> </pre> </div> </div> </div> <p>exemple de requête:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-4">java</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="n">Customer</span> <span class="n">customer</span> <span class="o">=</span> <span class="n">Customer</span><span class="o">.</span><span class="na">find</span><span class="o">.</span><span class="na">byId</span><span class="o">(</span><span class="mi">32</span><span class="o">);</span> <span class="n">List</span><span class="o">&lt;</span><span class="n">Customer</span><span class="o">&gt;</span> <span class="n">customers</span> <span class="o">=</span> <span class="n">Customer</span><span class="o">.</span><span class="na">find</span><span class="o">.</span><span class="na">all</span><span class="o">()</span> <span class="n">List</span><span class="o">&lt;</span><span class="n">Customer</span><span class="o">&gt;</span> <span class="n">customers</span> <span class="o">=</span> <span class="n">Customer</span><span class="o">.</span><span class="na">find</span><span class="o">.</span><span class="na">where</span><span class="o">().</span><span class="na">eq</span><span class="o">(</span><span class="s">&quot;name&quot;</span><span class="o">,</span> <span class="s">&quot;Dupond&quot;</span><span class="o">).</span><span class="na">findList</span><span class="o">();</span> </pre> </div> </div> </div> </div> <div class="section" id="les-vues"> <h2>Les Vues</h2> <p>Pour ce qui est du moteur de template, il est basé sur <strong>Scala</strong>. Les variables qui seront utilisées dans vos templates doivent être déclarées au début. Ensuite, chaque template est transformé en fonction Scala puis compilé en Bytecode java.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-5">list_user.scala.html</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-5"> <div class="highlight"> <pre class="literal-block"> &#64;(customers: List[Customer]) <span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span>Customer list<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span> &#64;for(customer <span class="p">&lt;</span><span class="nt">-</span> <span class="na">customers</span><span class="err">)</span> <span class="err">{</span> <span class="err">&lt;</span><span class="na">li</span><span class="p">&gt;</span>&#64;customer.first_name &#64;customer.last_name<span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span> } <span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&quot;&#64;{routes.Application.add_user}&quot;</span><span class="p">&gt;</span>Add customer<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span> </pre> </div> </div> </div> </div> <div class="section" id="les-forms"> <h2>Les forms</h2> <p>Comme avec django, les formulaires peuvent être généré à partir du modèle et être instancier à partir d'une requête POST. un get() permet de récupérer une instance du modèle et un save sur cette instance l'incrustera en BDD.</p> <p>Exemple d'utilisation de form dans mon contrôleur</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-6">Application.java</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-6"> <div class="highlight"> <pre class="literal-block"> <span class="kd">public</span> <span class="kd">class</span> <span class="nc">Application</span> <span class="kd">extends</span> <span class="n">Controller</span> <span class="o">{</span> <span class="kd">public</span> <span class="kd">static</span> <span class="n">Result</span> <span class="nf">list_user</span><span class="o">()</span> <span class="o">{</span> <span class="n">List</span><span class="o">&lt;</span><span class="n">Customer</span><span class="o">&gt;</span> <span class="n">customers</span> <span class="o">=</span> <span class="n">Customer</span><span class="o">.</span><span class="na">find</span><span class="o">.</span><span class="na">all</span><span class="o">();</span> <span class="k">return</span> <span class="n">ok</span><span class="o">(</span><span class="n">views</span><span class="o">.</span><span class="na">html</span><span class="o">.</span><span class="na">Application</span><span class="o">.</span><span class="na">list_user</span><span class="o">.</span><span class="na">render</span><span class="o">(</span><span class="n">customers</span><span class="o">));</span> <span class="o">}</span> <span class="kd">public</span> <span class="kd">static</span> <span class="n">Result</span> <span class="nf">add_user</span><span class="o">()</span> <span class="o">{</span> <span class="n">Form</span><span class="o">&lt;</span><span class="n">Customer</span><span class="o">&gt;</span> <span class="n">CustomerForm</span> <span class="o">=</span> <span class="n">Form</span><span class="o">.</span><span class="na">form</span><span class="o">(</span><span class="n">Customer</span><span class="o">.</span><span class="na">class</span><span class="o">);</span> <span class="k">return</span> <span class="n">ok</span><span class="o">(</span><span class="n">views</span><span class="o">.</span><span class="na">html</span><span class="o">.</span><span class="na">Application</span><span class="o">.</span><span class="na">add_user</span><span class="o">.</span><span class="na">render</span><span class="o">(</span><span class="n">CustomerForm</span><span class="o">));</span> <span class="o">}</span> <span class="kd">public</span> <span class="kd">static</span> <span class="n">Result</span> <span class="nf">save_user</span><span class="o">()</span> <span class="o">{</span> <span class="n">Form</span><span class="o">&lt;</span><span class="n">Customer</span><span class="o">&gt;</span> <span class="n">CustomerForm</span> <span class="o">=</span> <span class="n">Form</span><span class="o">.</span><span class="na">form</span><span class="o">(</span><span class="n">Customer</span><span class="o">.</span><span class="na">class</span><span class="o">).</span><span class="na">bindFromRequest</span><span class="o">();</span> <span class="k">if</span><span class="o">(</span><span class="n">CustomerForm</span><span class="o">.</span><span class="na">hasErrors</span><span class="o">())</span> <span class="o">{</span> <span class="k">return</span> <span class="n">ok</span><span class="o">(</span><span class="n">views</span><span class="o">.</span><span class="na">html</span><span class="o">.</span><span class="na">Application</span><span class="o">.</span><span class="na">add_user</span><span class="o">.</span><span class="na">render</span><span class="o">(</span><span class="n">CustomerForm</span><span class="o">));</span> <span class="o">}</span> <span class="k">else</span> <span class="o">{</span> <span class="n">Customer</span> <span class="n">newCustomer</span> <span class="o">=</span> <span class="n">CustomerForm</span><span class="o">.</span><span class="na">get</span><span class="o">();</span> <span class="n">newCustomer</span><span class="o">.</span><span class="na">save</span><span class="o">();</span> <span class="k">return</span> <span class="n">ok</span><span class="o">(</span><span class="n">views</span><span class="o">.</span><span class="na">html</span><span class="o">.</span><span class="na">Application</span><span class="o">.</span><span class="na">save_user</span><span class="o">.</span><span class="na">render</span><span class="o">(</span><span class="n">newCustomer</span><span class="o">));</span> <span class="o">}</span> <span class="o">}</span> <span class="o">}</span> </pre> </div> </div> </div> </div> <div class="section" id="conclusion"> <h2>Conclusion</h2> <p>Le peu que j'ai testé de Play m'a fait très bonne impression. L'apprentissage est rapide plus qu'en Django me semble t-il. par contre, je pense que la réutilisabilité des applications est moins efficace et cela est dû à la liberté laissé par Play sur le choix de l'ORM. Il n'y a pas non plus d'équivalent au Django CBV dans play qui permet un développement Dry est rapide. Néanmoins, play reste un framwork que je vous recommande d'essayer et sur lequel je pense m'attarder un peu plus.</p> </div> Les bases de la POO en python2014-06-28T13:12:00+02:002014-06-28T13:12:00+02:00Vincent Mailloltag:autourducode.com,2014-06-28:/les-bases-de-la-POO-en-python.html<p class="first last">En programmation par objet, on manipule des objets mais qu'est-ce qu'un objet ?</p> <p>En <strong>programmation orienté objet</strong> (POO) on manipule des <strong>objets</strong> mais qu'est-ce qu'un objet ?</p> <p>Un objet peu représenter quelque chose de la vie réelle, exemple La touche 'A' de votre clavier. On distingue deux choses:</p> <ul class="simple"> <li>Comment est l'objet (son état)</li> <li>Ce que l'on peut faire avec l'objet</li> </ul> <p>Comment est la touche: avec un 'A', enfoncée ou relâchée. Ce que l'on peut faire avec la touche: L'enfoncer ou la relâcher. Les actions que l'on fait sur un objet s'appelle des méthodes, ce sont des fonctions qui permettent de modifier l'état de l'objet.</p> <p>Les objets sont fabriqués par des <strong>classes</strong> que l'on a définies dans notre programme. La fabrication d'un objet s'appelle <strong>l'instanciation</strong>. En python, on utilise le <strong>mot clef class</strong> pour définir une classe.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Touche</span><span class="p">:</span> <span class="k">pass</span> </pre> </div> </div> </div> <p>Et on fabrique l'objet comme ceci.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-2">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="n">ma_touche</span> <span class="o">=</span> <span class="n">Touche</span><span class="p">()</span> <span class="nb">print</span> <span class="n">ma_touche</span> <span class="o">&lt;</span><span class="n">__main__</span><span class="o">.</span><span class="n">Touche</span> <span class="nb">object</span> <span class="n">at</span> <span class="mh">0xb76f202c</span><span class="o">&gt;</span> </pre> </div> </div> </div> <p>Bon c'est super, mais pour le moment on a une classe qui fabrique un objet sans état avec lequel on ne peut rien faire. Pour remédier à cette frustration on va ajouter de quoi initialiser l'état de notre objet: la méthode <strong>__init__</strong>.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-3">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Touche</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">lettre</span><span class="p">,</span> <span class="n">est_enfonce</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">lettre</span> <span class="o">=</span> <span class="n">lettre</span> <span class="bp">self</span><span class="o">.</span><span class="n">est_enfonce</span> <span class="o">=</span> <span class="n">est_enfonce</span> </pre> </div> </div> </div> <p>En python, la méthode pour initialiser notre objet s'appelle <strong>__init__</strong>. Comme toutes les méthodes, elle prend comme premier paramètre <strong>self</strong> qui n'est rien d'autre que notre objet puis elle ajoute à cet objet les attributs <em>lettre</em> et <em>est_enfonce.</em></p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-4">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="n">ma_touche_p</span> <span class="o">=</span> <span class="n">Touche</span><span class="p">(</span><span class="s1">'p'</span><span class="p">,</span> <span class="kc">True</span><span class="p">)</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">ma_touche_y</span> <span class="o">=</span> <span class="n">Touche</span><span class="p">(</span><span class="s1">'y'</span><span class="p">,</span> <span class="kc">True</span><span class="p">)</span> <span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span> <span class="n">ma_touche_p</span><span class="o">.</span><span class="n">lettre</span> <span class="s1">'p'</span> <span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span> <span class="n">ma_touche_p</span><span class="o">.</span><span class="n">est_enfonce</span> <span class="kc">True</span> <span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span> <span class="n">ma_touche_y</span><span class="o">.</span><span class="n">lettre</span> <span class="s1">'y'</span> </pre> </div> </div> </div> <p>Comme vous l'avez remarqué, on accède aux attributs d'un objet en utilisant un point (mon_objet.mon_attribut), on peut aussi les modifier de cette façon.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-5">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-5"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="n">ma_touche_y</span><span class="o">.</span><span class="n">est_enfonce</span> <span class="o">=</span> <span class="kc">False</span> <span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span> <span class="n">ma_touche_y</span><span class="o">.</span><span class="n">est_enfonce</span> <span class="kc">False</span> </pre> </div> </div> </div> <p>Mais voilà, faire comme ceci peut poser un problème.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-6">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-6"> <div class="highlight"> <pre class="literal-block"> <span class="n">ma_touche_y</span><span class="o">.</span><span class="n">lettre</span> <span class="o">=</span> <span class="s2">&quot;Z&quot;</span> <span class="n">ma_touche_y</span><span class="o">.</span><span class="n">est_enfonce</span> <span class="o">=</span> <span class="s2">&quot;J'sais pas mon chat est sur le clavier&quot;</span> </pre> </div> </div> </div> <p>Effectivement on ne s'attend pas à ce que la touche 'Y' devienne 'Z' et quand on demande si la touche est enfoncée on s'attend à vrai ou faux. Pour remédier à cela, on va mettre un _ devant les noms de nos attributs, c'est une convention en Python qui signifie je ne veux pas que l'on touche directement a ces attributs. Puis on va ajouter des méthodes pour modifier l'attribut est_enfoncé de manière correcte.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-7">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-7"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Touche</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">lettre</span><span class="p">,</span> <span class="n">est_enfonce</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_lettre</span> <span class="o">=</span> <span class="n">lettre</span> <span class="bp">self</span><span class="o">.</span><span class="n">_est_enfonce</span> <span class="o">=</span> <span class="n">est_enfonce</span> <span class="k">def</span> <span class="nf">enfonce</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_est_enfonce</span> <span class="o">=</span> <span class="kc">True</span> <span class="k">def</span> <span class="nf">relache</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_est_enfonce</span> <span class="o">=</span> <span class="kc">False</span> <span class="k">def</span> <span class="nf">afficher</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_est_enfonce</span><span class="p">:</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;La touche </span><span class="si">{}</span><span class="s2"> est enfoncé&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_lettre</span><span class="p">))</span> <span class="k">else</span><span class="p">:</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;La touche </span><span class="si">{}</span><span class="s2"> est relaché&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_lettre</span><span class="p">))</span> </pre> </div> </div> </div> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-8">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-8"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="n">touche_t</span> <span class="o">=</span> <span class="n">Touche</span><span class="p">(</span><span class="s1">'t'</span><span class="p">,</span> <span class="kc">True</span><span class="p">)</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">touche_t</span><span class="o">.</span><span class="n">enfonce</span><span class="p">()</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">touche_t</span><span class="o">.</span><span class="n">relache</span><span class="p">()</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">touche</span><span class="o">.</span><span class="n">afficher</span><span class="p">()</span> <span class="n">La</span> <span class="n">touche</span> <span class="n">t</span> <span class="n">est</span> <span class="n">enfoncé</span> </pre> </div> </div> </div> <p>J'imagine que certain d'entre vous ont déjà flairé l'arnaque et pense à faire ceci. <img alt="wink" src="/theme/img/smiley/smiley-wink.gif" /></p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-9">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-9"> <div class="highlight"> <pre class="literal-block"> <span class="n">ma_touche</span> <span class="o">=</span> <span class="n">Touche</span><span class="p">(</span><span class="s2">&quot;hello&quot;</span><span class="p">,</span> <span class="mi">42</span><span class="p">)</span> </pre> </div> </div> </div> <p>Pour remédier à cette porte ouverte à toutes les fenêtres on va vérifier que les données passées à __init__ soie du bon type. En python, tout est objet et connaître le type d'un objet revient à savoir quelle classe à fabriquer mon objet. La fonction pour savoir si un objet est une instance d'une classe est <strong>isinstance</strong>.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-10">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-10"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">ma_touche_t</span><span class="p">,</span> <span class="n">Touche</span><span class="p">)</span> <span class="kc">True</span> <span class="o">&gt;&gt;&gt;</span> <span class="nb">isinstance</span><span class="p">(</span><span class="mi">42</span><span class="p">,</span> <span class="n">Touche</span><span class="p">)</span> <span class="kc">False</span> <span class="o">&gt;&gt;&gt;</span> <span class="nb">isinstance</span><span class="p">(</span><span class="mi">42</span><span class="p">,</span> <span class="nb">int</span><span class="p">)</span> <span class="kc">True</span> </pre> </div> </div> </div> <p>Grace à <strong>isinstance</strong> on va pouvoir tester les paramètres d'entrée de notre methode __init__.</p> <p>Et s'ils ne sont pas bon on fait quoi ? <img alt="undecided" src="/theme/img/smiley/smiley-undecided.gif" /></p> <p>On va générer une exception. Une exception si vous n'en avez jamais vu c'est ça</p> <pre class="literal-block"> int(&quot;Chuck Norris&quot;) Traceback (most recent call last): File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;; ValueError: invalid literal for int() with base 10: 'Chuck Norris' </pre> <p>La fonction <strong>int</strong> viens de lancer une <strong>exception ValueError</strong> pour dire que non, elle ne fera pas de &quot;Chuck Norris&quot; un nombre entier. Pour lancer une exception on utilise le mot clef <strong>raise</strong> suivi d'un objet exception.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-11">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-11"> <div class="highlight"> <pre class="literal-block"> <span class="n">mon_exception</span> <span class="o">=</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;Je veux un multiple de 10&quot;</span><span class="p">)</span> <span class="k">raise</span> <span class="n">mon_exception</span> <span class="c1"># On n'est pas obligé de passer par une variable</span> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;Je veux un multiple de 10&quot;</span><span class="p">)</span> </pre> </div> </div> </div> <p>Ok donc si on applique ça à notre fonction __init__, ça donne ça.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-12">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-12"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Touche</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">lettre</span><span class="p">,</span> <span class="n">est_enfonce</span><span class="p">):</span> <span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">lettre</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s2">&quot;First argument must be a sting&quot;</span><span class="p">)</span> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">lettre</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;First argument must have len equal to 1&quot;</span><span class="p">)</span> <span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">est_enfonce</span><span class="p">,</span> <span class="nb">bool</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s2">&quot;Second argument must be a boolean&quot;</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">_lettre</span> <span class="o">=</span> <span class="n">lettre</span> <span class="bp">self</span><span class="o">.</span><span class="n">_est_enfonce</span> <span class="o">=</span> <span class="n">est_enfonce</span> <span class="k">def</span> <span class="nf">enfonce</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_est_enfonce</span> <span class="o">=</span> <span class="kc">True</span> <span class="k">def</span> <span class="nf">relache</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_est_enfonce</span> <span class="o">=</span> <span class="kc">False</span> <span class="k">def</span> <span class="nf">afficher</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_est_enfonce</span><span class="p">:</span> <span class="nb">print</span> <span class="s2">&quot;La touche </span><span class="si">%s</span><span class="s2"> est enfoncé&quot;</span> <span class="k">else</span><span class="p">:</span> <span class="nb">print</span> <span class="s2">&quot;La touche </span><span class="si">%s</span><span class="s2"> est relaché&quot;</span> </pre> </div> </div> </div> <p>J'ai utilisé deux types d'erreur différent, <strong>TypeError</strong> quant ce n'est pas le bon type qui est passé en paramètre et <strong>ValueError</strong> quant c'est le bon type mais pas la bonne valeur.</p> <p>Donc en résumé,</p> <ul class="simple"> <li>Les objets sont instanciés (fabriqué) par des classes</li> <li>L'état d'un objet est représenté par un ensemble d'attributs.</li> <li>Pour que l'état d'un objet reste cohérent, on utilise des méthodes pour manipuler ses attributs.</li> <li>isinstance permet de savoir de quel type est un objet.</li> </ul> <p>Ce que l'on a vu sur le fait de garder l'état d'un objet cohérent en utilisant des méthodes s'appelle <strong>l'encapsulation</strong>, c'est la base de la base de la POO. Il nous reste deux choses à voir qui sont <a class="reference external" href="http://autourducode.com/les-bases-de-la-poo-en-python-heritage.html">l'Héritage</a> et la <a class="reference external" href="http://autourducode.com/les-bases-de-la-poo-en-python-redefinition-de-methode.html">redéfinition de méthode</a>.</p> <p>J'espère que ça vous a plu, à vos IDE et bon code <img alt="smile" src="/theme/img/smiley/smiley-smile.gif" /></p> Les iterareur en python2014-06-26T22:34:00+02:002016-09-09T11:51:00+02:00Vincent Mailloltag:autourducode.com,2014-06-26:/les-iterareur-en-python.html<p class="first last">Comment le design pattern itérateur est intégré au langage Python est pourquoi dit-on que les itérateurs ne sont pas robuste.</p> <p>Un <strong>itérateur</strong> c'est un objet qui est créé par un agrégat, un agrégat est n'importe quel objet composé de plusieurs choses comme une liste ou un wagon composé de passagers. L'itérateur va servir à parcourir cet objet, généralement grâce à deux méthodes <strong>next</strong> qui retourne le prochain élément et <strong>hasNext</strong> qui indique s'il reste un objet à retourner. Dans les langages au typage statique il y a généralement une classe abstraite iterator de laquelle on peut hériter.</p> <div class="section" id="l-impact-du-duck-typing-sur-les-iterators-en-python"> <h2>L'impact du duck typing sur les iterators en python</h2> <ol class="arabic simple"> <li>C'est du duck typing donc pas besoin d'hériter d'une classe Aggregate ou Iterator.</li> <li>Pas de méthode <strong>hasNext</strong>, un iterator lance une exception <strong>StopIteration</strong></li> <li>La méthode next c'est <strong>__next__</strong> (ou next dans python2)</li> <li>Pour créer un iterator, la méthode de l'agregate est <strong>__iter__</strong></li> </ol> <p>Le coup du <strong>__iter__</strong> et <strong>__next__</strong> en privé ça peut sembler bizarre, mais le truc c'est que c'est directement géré par <strong>la boucle for</strong> et qu'il existe les fonctions <strong>iter</strong> et <strong>next</strong> qui appele les méthodes privé de l'objet</p> <p>Ainsi, le code ci-dessous:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="n">lst</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">lst</span><span class="p">:</span> <span class="nb">print</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> </pre> </div> </div> </div> <p>Peut s'écrire comme ceci:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-2">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="n">lst</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span> <span class="n">iter_lst</span> <span class="o">=</span> <span class="nb">iter</span><span class="p">(</span><span class="n">lst</span><span class="p">)</span> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> <span class="k">try</span><span class="p">:</span> <span class="nb">print</span><span class="p">(</span><span class="nb">next</span><span class="p">(</span><span class="n">iter_lst</span><span class="p">))</span> <span class="k">except</span> <span class="ne">StopIteration</span><span class="p">:</span> <span class="k">break</span> </pre> </div> </div> </div> </div> <div class="section" id="ecrire-ses-propres-iterators-en-python"> <h2>Écrire ses propres iterators en python</h2> <p>Pour faire ses propres itérateurs, on va prendre l'exemple d'une classe RGB sur laquelle on veut parcourir chaque valeur. On peut faire comme ça.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-3">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">RGB</span><span class="p">:</span> <span class="c1"># L'iterator est une nested class, il</span> <span class="c1"># n'a pas besoin d'être utilisé en dehors de RGB.</span> <span class="k">class</span> <span class="nc">RGBIterator</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">rgb</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_rgb</span> <span class="o">=</span> <span class="n">rgb</span> <span class="c1"># On stoque l'agreggate.</span> <span class="bp">self</span><span class="o">.</span><span class="n">_attrs</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'r'</span><span class="p">,</span><span class="s1">'g'</span><span class="p">,</span> <span class="s1">'b'</span><span class="p">]</span> <span class="c1"># ça va nous servir à savoir quelle valeur on a déjà retourné.</span> <span class="k">def</span> <span class="nf">__next__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_attrs</span><span class="p">:</span> <span class="k">return</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_rgb</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">_attrs</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span> <span class="k">raise</span> <span class="ne">StopIteration</span><span class="p">()</span> <span class="nb">next</span> <span class="o">=</span> <span class="fm">__next__</span> <span class="c1"># Compatibilité python2/3</span> <span class="k">def</span> <span class="nf">__iter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="c1"># Un itérateur est généralement aussi un itérable</span> <span class="k">return</span> <span class="bp">self</span> <span class="c1"># en se retournant lui même</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">r</span><span class="p">,</span> <span class="n">g</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">r</span> <span class="o">=</span> <span class="n">r</span> <span class="bp">self</span><span class="o">.</span><span class="n">g</span> <span class="o">=</span> <span class="n">g</span> <span class="bp">self</span><span class="o">.</span><span class="n">b</span> <span class="o">=</span> <span class="n">b</span> <span class="k">def</span> <span class="nf">__iter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="n">RGB</span><span class="o">.</span><span class="n">RGBIterator</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="c1"># Exemple d'utilisation:</span> <span class="n">rgb</span> <span class="o">=</span> <span class="n">RGB</span><span class="p">(</span><span class="mi">64</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">18</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">rgb</span><span class="p">:</span> <span class="nb">print</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> </pre> </div> </div> </div> <p>Mais il y a plus simple ! <img alt="laughing" src="/theme/img/smiley/smiley-laughing.gif" /> En fait et il n'y a pas besoin d'implémenter __iter__. Du moment que la <strong>méthode __getitem__</strong> est implémentée votre objet est itérable. __getitem__ sert à utiliser des [ ] sur votre objet comme dans my_obj[2] ou my_obj[a_key]</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-4">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">RGB</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">r</span><span class="p">,</span> <span class="n">g</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">r</span> <span class="o">=</span> <span class="n">r</span> <span class="bp">self</span><span class="o">.</span><span class="n">g</span> <span class="o">=</span> <span class="n">g</span> <span class="bp">self</span><span class="o">.</span><span class="n">b</span> <span class="o">=</span> <span class="n">b</span> <span class="k">def</span> <span class="nf">__getitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">indice</span><span class="p">):</span> <span class="k">return</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">r</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">g</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">b</span><span class="p">)[</span><span class="n">indice</span><span class="p">]</span> </pre> </div> </div> </div> <p>On peut également itérer dans l'autre sens avec la fonction <strong>reversed</strong>:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-5">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-5"> <div class="highlight"> <pre class="literal-block"> <span class="n">text</span> <span class="o">=</span> <span class="s2">&quot;elu par cette crapule&quot;</span> <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="nb">reversed</span><span class="p">(</span><span class="n">text</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="n">e</span><span class="p">)</span> </pre> </div> </div> </div> <p>Pour ça il faut que notre classe soit un iterator et qu'elle ait une méthode <strong>__len__</strong> qui renvoie sa taille car si l'on part de la fin, il faut bien savoir par où commencer. <img alt="wink" src="/theme/img/smiley/smiley-wink.gif" /></p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-6">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-6"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">RGB</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">r</span><span class="p">,</span> <span class="n">g</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">r</span> <span class="o">=</span> <span class="n">r</span> <span class="bp">self</span><span class="o">.</span><span class="n">g</span> <span class="o">=</span> <span class="n">g</span> <span class="bp">self</span><span class="o">.</span><span class="n">b</span> <span class="o">=</span> <span class="n">b</span> <span class="k">def</span> <span class="nf">__getitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">indice</span><span class="p">):</span> <span class="k">return</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">r</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">g</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">b</span><span class="p">)[</span><span class="n">indice</span><span class="p">]</span> <span class="k">def</span> <span class="nf">__len__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="mi">3</span> </pre> </div> </div> </div> <p>Bon remarquez que ce n'est pas très pertinent de faire un reversed sur un rgb et d'avoir un __len__. Mais c'est pour l'exemple.</p> </div> <div class="section" id="les-iterateurs-en-python-ne-sont-pas-robustes"> <h2>Les itérateurs en python ne sont pas robustes</h2> <p>On parle de <strong>robustesse d'un itérateur</strong> lorsque celui-ci est capable de prendre en compte le fait que l'agrégat qu'il parcourt a changé. En python, si l'on parcoure une liste qui perd un élément, l'itérateur va se décaller.</p> <p>Par exemple, si l'on supprime le nombre 12 dans la liste lors de l'itération, le nombre 13 n'est pas parcourue:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-7">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-7"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="n">l</span> <span class="o">=</span> <span class="nb">list</span><span class="p">([</span><span class="mi">11</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">14</span><span class="p">])</span> <span class="o">&gt;&gt;&gt;</span> <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">l</span><span class="p">:</span> <span class="o">...</span> <span class="nb">print</span><span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="o">...</span> <span class="k">if</span> <span class="n">e</span> <span class="o">==</span> <span class="mi">12</span><span class="p">:</span> <span class="o">...</span> <span class="k">del</span> <span class="n">l</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">...</span> <span class="mi">11</span> <span class="mi">12</span> <span class="mi">14</span> </pre> </div> </div> </div> <p>Rien ne nous empèche de créer une liste qui retourne un itérateur robuste. On va utiliser <a class="reference external" href="http://autourducode.com/le-design-pattern-observer-et-ses-variantes.html">le design pattern observer</a>, la liste sera observable et les itérateurs seront également observateurs.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-8">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-8"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">MyList</span><span class="p">:</span> <span class="k">class</span> <span class="nc">MyListIterator</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">my_list</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_my_list</span> <span class="o">=</span> <span class="n">my_list</span> <span class="bp">self</span><span class="o">.</span><span class="n">_i</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span> <span class="k">def</span> <span class="nf">__next__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_i</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">try</span><span class="p">:</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_my_list</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">_i</span><span class="p">]</span> <span class="k">except</span> <span class="ne">IndexError</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">StopIteration</span> <span class="k">def</span> <span class="nf">update</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">offset</span><span class="p">):</span> <span class="k">if</span> <span class="n">offset</span> <span class="o">==</span> <span class="bp">self</span><span class="o">.</span><span class="n">_i</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">_i</span> <span class="o">-=</span> <span class="mi">1</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">iterable</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_list</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">iterable</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">_iterators</span> <span class="o">=</span> <span class="p">[]</span> <span class="k">def</span> <span class="nf">__getitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_list</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="k">def</span> <span class="nf">__delitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span> <span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">_list</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="c1"># Notifier les itérateurs qu'un élément est enlevé de la liste.</span> <span class="k">for</span> <span class="n">iterator</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_iterators</span><span class="p">:</span> <span class="n">iterator</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">key</span><span class="p">)</span> <span class="k">def</span> <span class="nf">__iter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">iterator</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">MyListIterator</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">_iterators</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">iterator</span><span class="p">)</span> <span class="k">return</span> <span class="n">iterator</span> </pre> </div> </div> </div> <p>Le code d'exemple ci-dessus ne couvre pas le cas de la suppression de plusieurs éléments. Vous pouvez modifier la méthode MyListIterator.update pour qu'elle traite les objets de type <a class="reference external" href="https://docs.python.org/3/library/functions.html#slice">slice</a>.</p> </div> <div class="section" id="savoir-si-un-objet-est-iterable"> <h2>Savoir si un objet est iterable</h2> <p>Un dernier truc, pour savoir si une classe est itérable, il y a la classe abstraite <strong>Iterable</strong> dans le module collections.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-9">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-9"> <div class="highlight"> <pre class="literal-block"> <span class="kn">import</span> <span class="nn">collections</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">rgb</span><span class="p">,</span> <span class="n">collections</span><span class="o">.</span><span class="n">Iterable</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;:-)&quot;</span><span class="p">)</span> </pre> </div> </div> </div> <p>Voilà, on a vu comment le design pattern iterator est intégré dans le langage python.</p> </div> Python et les weakref2014-06-26T20:26:00+02:002014-06-26T20:26:00+02:00Vincent Mailloltag:autourducode.com,2014-06-26:/python-les-weakref.html<p class="first last">Les weakref sont comme des variables, elles sont une reference sur un objet python.</p> <p>Les weakref sont comme des variables, elles sont une reference sur un objet python. La différence, c'est que les weakref n'affectent pas le compteur de référence de celui-ci.</p> <div class="section" id="mais-le-compteur-de-reference-c-est-quoi"> <h2>Mais le compteur de référence, c'est quoi ?</h2> <p>En python, chaque fois qu'une variable pointe sur un objet, le conteur de référence est incrémentée de +1 et il est décrémenté lorsqu'une variable ne pointe plus vers lui. Lorsque le compteur de référence est à 0, la mémoire de l'objet sera libérée par le garbage collector, une sorte de râteau qui supprime de la mémoire tout ce qui n'a plus lieu d'être.</p> <p>On peut observer cela avec la fonction <strong>getrefcount</strong>, qui retourne le nombre de référence qui pointe sur un objet. Par contre comme le fait d'appeler getrefcount sur l'objet crée une référence à celui-ci via une variable temporaire, getrefcount retourne une valeur de plus à celle attendue.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-1">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-1"> <div class="highlight"> <pre class="literal-block"> <span class="kn">from</span> <span class="nn">sys</span> <span class="k">import</span> <span class="n">getrefcount</span> <span class="k">class</span> <span class="nc">Foo</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">label</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">label</span> <span class="o">=</span> <span class="n">label</span> <span class="k">def</span> <span class="nf">__del__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">{}</span><span class="s2"> is destroy&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">label</span><span class="p">))</span> <span class="n">a</span> <span class="o">=</span> <span class="n">Foo</span><span class="p">(</span><span class="s1">'Toto'</span><span class="p">)</span> <span class="c1"># la variable 'a' référe l'objet 'Toto'</span> <span class="n">getrefcount</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="c1"># 2</span> <span class="n">b</span> <span class="o">=</span> <span class="n">a</span> <span class="c1"># 'b' référe aussi l'objet 'Toto'</span> <span class="n">getrefcount</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> <span class="c1"># 3</span> <span class="n">a</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">getrefcount</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> <span class="c1"># 2</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">2</span> <span class="c1"># plus rien ne pointe sur l'objet Toto. Toto is destroy est affichée.</span> </pre> </div> </div> </div> </div> <div class="section" id="et-les-weakref-dans-tout-ca"> <h2>Et les weakref dans tout ça ?</h2> <p>Elles permettent d'avoir une référence vers un objet sans incrémenter son compteur. Demonstration:</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-2">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-2"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="n">a</span> <span class="o">=</span> <span class="n">Foo</span><span class="p">(</span><span class="s1">'toto'</span><span class="p">)</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">getrefcount</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="mi">2</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">ref_on_a</span> <span class="o">=</span> <span class="n">weakref</span><span class="o">.</span><span class="n">ref</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="c1"># Création d'une référence.</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">getrefcount</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="mi">2</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">ref_on_a</span><span class="p">()</span> <span class="ow">is</span> <span class="n">a</span> <span class="c1"># Les ref sont des callables, on utilise () pour récupérer l'objet.</span> <span class="kc">True</span> <span class="o">&gt;&gt;&gt;</span> <span class="k">del</span> <span class="n">a</span> <span class="n">toto</span> <span class="ow">is</span> <span class="n">destroy</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">ref_on_a</span><span class="p">()</span> <span class="ow">is</span> <span class="kc">None</span> <span class="c1"># Si l'objet référencé est détruit, l'appel d'une ref retourne None.</span> <span class="kc">True</span> </pre> </div> </div> </div> </div> <div class="section" id="dans-quel-cas-ca-s-utilise"> <h2>Dans quel cas ça s'utilise ?</h2> <p>Le problème vient lorsque des objets se font mutuellement référencent.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-3">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-3"> <div class="highlight"> <pre class="literal-block"> <span class="k">class</span> <span class="nc">Foo</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">label</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">label</span> <span class="o">=</span> <span class="n">label</span> <span class="bp">self</span><span class="o">.</span><span class="n">_foo</span> <span class="o">=</span> <span class="kc">None</span> <span class="k">def</span> <span class="nf">set_foo</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">foo</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">foo</span> <span class="o">=</span> <span class="n">foo</span> <span class="k">def</span> <span class="nf">__del__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">{}</span><span class="s2"> is destroy&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">label</span><span class="p">))</span> <span class="n">a</span> <span class="o">=</span> <span class="n">Foo</span><span class="p">(</span><span class="s1">'toto'</span><span class="p">)</span> <span class="n">b</span> <span class="o">=</span> <span class="n">Foo</span><span class="p">(</span><span class="s1">'tata'</span><span class="p">)</span> <span class="n">a</span><span class="o">.</span><span class="n">set_foo</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> <span class="n">b</span><span class="o">.</span><span class="n">set_foo</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="k">del</span> <span class="n">a</span> <span class="k">del</span> <span class="n">b</span> </pre> </div> </div> </div> <p>Les compteurs de référence sur toto et tata ne sont pas à 0 est le garbage collector a plus de dificulté à libèrer la mémoire. C'est dans ce genre de cas que l'on peut utiliser les weakref.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-4">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-4"> <div class="highlight"> <pre class="literal-block"> <span class="kn">import</span> <span class="nn">weakref</span> <span class="k">class</span> <span class="nc">Foo</span><span class="p">:</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">label</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">label</span> <span class="o">=</span> <span class="n">label</span> <span class="bp">self</span><span class="o">.</span><span class="n">_foo</span> <span class="o">=</span> <span class="kc">None</span> <span class="k">def</span> <span class="nf">set_foo</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">foo</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">foo</span> <span class="o">=</span> <span class="n">weakref</span><span class="o">.</span><span class="n">ref</span><span class="p">(</span><span class="n">foo</span><span class="p">)</span> <span class="k">def</span> <span class="nf">__del__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">{}</span><span class="s2"> is destroy&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">label</span><span class="p">))</span> <span class="n">a</span> <span class="o">=</span> <span class="n">Foo</span><span class="p">(</span><span class="s1">'toto'</span><span class="p">)</span> <span class="n">b</span> <span class="o">=</span> <span class="n">Foo</span><span class="p">(</span><span class="s1">'tata'</span><span class="p">)</span> <span class="n">a</span><span class="o">.</span><span class="n">set_foo</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> <span class="n">b</span><span class="o">.</span><span class="n">set_foo</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="k">del</span> <span class="n">a</span> <span class="c1"># toto is destroy</span> <span class="k">del</span> <span class="n">b</span> <span class="c1"># tata is destroy</span> </pre> </div> </div> </div> <p>Attention au piège dans les environnements multithread.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-5">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-5"> <div class="highlight"> <pre class="literal-block"> <span class="k">if</span> <span class="n">my_ref</span><span class="p">()</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span> <span class="n">my_ref</span><span class="p">()</span><span class="o">.</span><span class="n">a_method</span><span class="p">()</span> <span class="c1"># Si entre temps l'objet pointé par my_ref est détruit,</span> <span class="c1"># la méthode a_method() est appelée sur None.</span> </pre> </div> </div> </div> <p>Il existe une autre façon de faire des références, les proxy.</p> <ul class="nav nav-tabs simple"> <li class="active"><a class="reference internal" href="#code-6">Python 3 </a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="code-6"> <div class="highlight"> <pre class="literal-block"> <span class="o">&gt;&gt;&gt;</span> <span class="n">a</span> <span class="o">=</span> <span class="n">Foo</span><span class="p">(</span><span class="s1">'toto'</span><span class="p">)</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">p</span> <span class="o">=</span> <span class="n">weakref</span><span class="o">.</span><span class="n">proxy</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">p</span><span class="o">.</span><span class="n">label</span> <span class="c1"># Tout les attribut de toto sont accécible via le proxy.</span> <span class="s1">'toto'</span> <span class="o">&gt;&gt;&gt;</span> <span class="k">del</span> <span class="n">a</span> <span class="n">toto</span> <span class="ow">is</span> <span class="n">destroy</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">p</span> <span class="o">&lt;</span><span class="n">weakproxy</span> <span class="n">at</span> <span class="mh">0x7f4704beeaa0</span> <span class="n">to</span> <span class="n">NoneType</span> <span class="n">at</span> <span class="mh">0x890b40</span><span class="o">&gt;</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">p</span><span class="o">.</span><span class="n">label</span> <span class="c1"># Si l'objet référencé est détruit, l'accé a un attribut lance une exception ReferenceError.</span> <span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> <span class="n">File</span> <span class="s2">&quot;&lt;stdin&gt;&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span> <span class="ne">ReferenceError</span><span class="p">:</span> <span class="n">weakly</span><span class="o">-</span><span class="n">referenced</span> <span class="nb">object</span> <span class="n">no</span> <span class="n">longer</span> <span class="n">exists</span> </pre> </div> </div> </div> <p>Une autre utilisation sont les systèmes de cache. Un objet couteux à créer est demandé. Cet objet est instancier est mise en cache. Si une autre personne demande l'objet le cache fournit l'objet si plus personne n'utilise l'objet, le cache est vidé automatiquement. Les classes WeakValueDictionary et WeakSet peuvent vous aider à implémenter ces caches</p> </div> Référencement2014-06-01T20:59:00+02:002014-06-01T20:59:00+02:00Vincent Mailloltag:autourducode.com,2014-06-01:/referencenment.html<p class="first last">C'est bien beau d'écrire des articles, mais encore faut-il qu'ils soient lus</p> <p>C'est bien beau d'écrire des articles, mais encore faut-il qu'ils soient lus</p> <p>Rien de telle qu'un annuaire pour améliorer le référencement et potentiellement augmenter le nombre de visiteurs et les annuaires, ce n'est pas ce qui manque.</p> <p>Privilégiez la qualité, il y a ceux qui acceptent tout et n'importe quoi et ceux qui sont plus exigent comme <a class="reference external" href="http://www.webrankinfo.com/">webrankinfo</a> il assure un meilleur référencement, mais vous aurez plus de travail à fournir pour y inscrire votre site.</p> <p>Préférez également les annuaires avec un bon pagerank pour cela vous pouvez utiliser le www.calcul-pagera</p>