Archive

Archive for the ‘Python’ Category

Python2, toujours sur les routes

Ne faites pas comme ce cycliste, passez à Python3 !

pneu python2

Si Hutchinson fournit des pneus Python2, la maintenance de sécurité de cPython s’arrête à la fin de cette année. Aucune fonctionnalité n’est plus ajoutée depuis plusieurs années.

Catégories :Python

Visualiser la répartition des cartes à Celestia avec Matplotlib (scatter)

Celestia est un jeu de société dans lequel les joueurs vont avancer ensemble de planète en planète. Chacun doit décider individuellement :

  • soit descendre du vaisseau, et ainsi s’assurer la prise d’une carte
  • soit prendre le risque de continuer, et avoir la chance d’avoir une carte de plus forte valeur.

Chaque paquet de cartes a une plage de valeurs différentes. Voici une représentation de ces répartitions :

La taille des points varie en fonction de la quantité d’occurences de la même valeur. Matplotlib dispose pour cela d’une fonction spécifique nommée scatter.

matplotlib.pyplot.scatter(
    [x0, x1, ..., xn],
    [y0, y1, ..., yn],
    s=[taille_de_x0y0, taille_de_x1y1, ..., taille_de_xn_yn ])

Une démo de « scatter » est aussi visible sur le site de matplolib.

Le code source complet

import collections
import matplotlib.pyplot as plt

# répartition des cartes
stats = {1: [1, 2, 4, 1, 2, 4, 2, 1, 1, 1, 2],
        2: [4, 2, 6, 4, 2, 4, 2, 6, 2, 2, 2],
        4: [2, 4, 9, 4, 6, 4, 4, 4, 6, 6],
        6: [2, 9, 9, 6, 6, 6, 9, 12, 6, 6],
        9: [9, 9, 9, 12, 9, 9, 9, 12, 12],
        12: [12, 12, 12, 12, 15, 15, 12, 12, 15],
#        15: [15 for i in range(6)],
#        20: [20 for i in range(6)],
#        25: [25 for i in range(6)]
}

x = [clef for clef, valeurs in stats.items() for i in range(len(valeurs))]
y = [valeur for valeurs in stats.values() for valeur in valeurs]


# compte les occurences de chaque valeur par planète
compteur = collections.Counter(zip(x, y))
# liste des tailles des points en fonction des occurences
tailles = [50 * compteur[(xx, yy)] for xx, yy in zip(x, y)]

# matplotlib en action !
plt.scatter(x, y, s=tailles)
plt.grid(color='grey', linestyle='dashed')
plt.title("Répartition des valeurs des cartes")
plt.xlabel("Niveaux")
plt.ylabel("Valeurs")


axes = plt.gca()
axes.set_ylim([0, 16])

plt.savefig('repartition_cartes_celestia.png')
plt.show()
Catégories :Python Étiquettes :

Avec with : plus d’indentations, moins d’instructions

En python, le mot-clef with permet d’ouvrir un bloc dans lequel un contexte spécifique est actif.

L’exemple de « context manager » le plus courant est celui d’une ouverture de fichier qui se ferme automagiquement :

with open('sortie.txt', 'w') as f:
    f.write('Coucou')
f.closed # vaut True

On retrouve cette possibilité dans de nombreux cas comme l’ouverture d’un serveur SMTP :

from smtplib import SMTP
with SMTP("domain.org") as smtp:
    # faire des trucs
#la connection smtp est fermée

L’utilisation d’un « context manager » est adapté à chaque fois qu’il est nécessaire de réaliser des tâches à l’ouverture ou la fermeture (libérer un verrou, fermer une connection, supprimer un fichier, changer une variable globale, etc.). Cela permet de se concentrer ce qu’on veut vraiment faire dans le contexte du with. Les tâches d’ouverture et fermetures sont respectivement déplacées dans les méthodes magiques __enter__() et __exit__(). Si l’on prend en compte l’ensemble du code source, il n’y a donc pas moins d’instructions. Et le titre de l’article peut être considérée comme de la publicité mensongère.

class Pipeau:

    def __enter__(self):
        print("<bonimenteur>")

    def __exit__(self, type, value, traceback):
        print("</bonimenteur>")

with Pipeau():
    print("Prochain article de blog à -50 % !")

# affiche :
# <bonimenteur>
# Prochain article de blog à -50 % !
# </bonimenteur>

Pour plus de détails sur l’utilisation de with, consultez la documentation Python et l’article de sametmax.

Catégories :Python

Coder une variante de FizzBuzz basée sur le jeu 6 qui prend

FizzBuzz est un jeu où l’on compte : lorsqu’un entier est un multiple de 3, on dit « Fizz », multiple de 5 « Buzz », multiple de 3 et 5 « FizzBuzz », dans les autres cas, on dit simplement l’entier. Il est aussi utilisé comme test pour évaluer si quelqu’un maîtrise les bases de la programmation. Une variante, un peu plus compliquée, mais du même type pourrait être réalisée avec le jeu 6 qui prend.

C’est un jeu de cartes dont le but est d’avoir le moins de têtes de bœufs. Les cartes sont numérotées de 1 à 104 (inclus).

Les règles d’estimation de la valeur (en tête de bœufs) des cartes sont relativement similaires aux règles du FizzBuzz :

  • si le nombre est un multiple de 5, la carte vaut 2 têtes de bœufs
  • si le nombre est un multiple de 10, la carte vaut 3 têtes de bœufs
  • si le chiffre des dizaines et des unités sont identiques, la carte vaut 5 têtes de bœufs
  • la carte 55 vaut 7 têtes de bœufs
  • les autres cartes valent 1 tête de bœufs chacune

TL; DR Si on a vraiment du temps à perdre, on finit avec un code de ce genre :

CARTES = 104

def tetes(numero):
    _score = 1
    for diviseur, points in ((10, 1), (5, 1), (11, 4)):
        if _est_multiple(numero, diviseur):
            _score += points
    if numero == 55:
        _score += 1
    return _score

def _est_multiple(numero, diviseur):
    return not numero % diviseur

if __name__ == "__main__": 
    for numero in range(1, CARTES + 1):
        print(numero, "★" * tetes(numero))

Le début de la sortie du terminal :

1 ★
2 ★
3 ★
4 ★
5 ★★
6 ★
7 ★
8 ★
9 ★
10 ★★★
11 ★★★★★
12 ★

Première version

Le code le plus rapide à écrire pour passer les tests devrait ressembler à :

def tetes(numero):
    if numero == 55:
        return 7
    if not numero % 10:
        return 3
    elif not numero % 5:
        return 2
    elif numero >= 10 and str(numero)[0] == str(numero)[1]:
        return 5
    else:
        return 1

if __name__ == "__main__": 
    for numero in range(1, 12):
        print(numero, "*" * tetes(numero))

Améliorations

La règle « le chiffre des dizaines et des unités sont identiques » est équivalent à être un multiple de 11 :

#if numero >= 10 and str(numero)[0] == str(numero)[1]:
if not numero % 11:

Autant factoriser le test de divisibilité :

def tetes(numero):
    _score = 1
    if _est_multiple(numero, 10):
        _score += 1
    if _est_multiple(numero, 5):
        _score += 1
    if _est_multiple(numero, 11):
        _score += 4
    if numero == 55:
        _score += 1
    return _score

def _est_multiple(numero, diviseur):
    return not numero % diviseur

On peut alors remplacer la succession de if par une boucle, et hop, on obtient un code final (qui est au début de l’article).

Tests

Pour valider le programme, quelques assertions de bon aloi sont à ajouter. Ces assertions auront été obtenues au fur et à mesure si on fait du TDD.

import sixquiprend

assert 1 == sixquiprend.tetes(1)
assert 1 == sixquiprend.tetes(2)
assert 2 == sixquiprend.tetes(5)
assert 3 == sixquiprend.tetes(10)
assert 5 == sixquiprend.tetes(11)
assert 5 == sixquiprend.tetes(22)
assert 7 == sixquiprend.tetes(55)
Catégories :Python Étiquettes :

Meld et Mercurial

Meld est un équivalent graphique de `diff` : il permet de faire des différences entre des fichiers de manière plus visuelle. Mercurial peut utiliser `meld` plutôt que l’outil par défaut lorsqu’il faut afficher des différences entre les fichiers du dépôt local et les fichier du dépôt distant.

Mise en œuvre

Il suffit d’activer l’extension Extdiff fournie avec Mercurial. Cela se fait en ajoutant les lignes suivantes au fichier ~/.hgrc (ou au fichier .hg/hgrc si on souhaite limiter la modification à un répertoire versionné) :

[extensions]
hgext.extdiff =

[extdiff]
cmd.meld =

Différences sur un fichier

Supposons cette modification d’un dépôt :

$ hg diff
diff -r 1915567d21b2 jours.py
--- a/jours.py	Sat Jul 08 15:37:19 2017 +0200
+++ b/jours.py	Sat Jul 08 15:37:59 2017 +0200
@@ -1,12 +1,10 @@
 """Module des jours"""
 
-# Trop simple, faire une fabrique abstraite ?
-
 JOURS = ("lundi",
     "mardi",
     "mercredi",
-    "jedi",
+    "jeudi",
     "vendredi",
     "samedi",
-    "dimenche"
+    "dimanche"
     )

En utilisant la commande `hg meld`, une fenêtre de `meld` s’ouvre, montrant l’état dans le dépôt à gauche et l’état du fichier sur le disque à droite :

Cliquer sur les flèches permet de copier le contenu d’un côté à l’autre automatiquement. Il est aussi possible de modifier le contenu directement.

Différences sur plusieurs fichiers

Avec les modifications suivantes par rapport au dernier commit :

$ hg stat
M annees.py
A siecles.py
R heures.py

Lorsque plusieurs fichiers sont modifiés, `hg meld` affiche un premier panneau permettant de voir les fichiers modifiés (en bleu), ajoutés (en vert), absents (grisé et rayé). En double-cliquant sur un des fichiers, un nouvel onglet s’ouvre, affichant la vue détaillée des différences entre des deux fichiers.

Visualisation de conflits

Si `meld` est disponible, Mercurial s’en servira aussi pour la résolution des conflits.
La partie à gauche est l’état du dépôt local, la partie à droite l’état du dépôt distant et la partie centrale l’ancêtre commun au deux fichiers.

Comme dans le cas précédent, il est possible de modifier les fichiers dans `meld` pour résoudre les conflits.

Versions

Cet article a été fait avec meld, version 3.16.4 et mercurial version 4.0. Il a aussi été validé avec meld, version 1.8.4 et mercurial version 2.8.2.

L’image d’en-tête est une superposition des deux logos (Meld et Mercurial), réalisée avec The Gimp.

Catégories :Python
Concevoir un site comme celui-ci avec WordPress.com
Commencer