Archive

Archive for the ‘Python’ Category

Faker : une bibliothèque python pour générer des noms aléatoirement

12 mars 2017 1 commentaire

Faker est une bibliothèque python pour générer aléatoirement des noms, adresses, etc. Elle facilite l’écriture de script pour remplir une base de données avec de fausses informations diversifiées.

Les données produites peuvent être en français. Comme ça, vous pourrez avoir Bertrand Picard plutôt que Lisa Smith. L’anglais est la langue par défaut mais il est aussi possible d’avoir des résultats en italien, espagnol, russe, ukrainien, etc.

from faker import Factory
fake = Factory.create('fr_FR')
fake.name() # 'Laetitia Roger'
fake.name() #'Victoire de la Cohen'
fake.first_name() # 'Jeannine'
fake.first_name() # 'Christophe'
fake.first_name_female() # 'Alexandrie'
fake.first_name_male() # 'Rémy'
fake.address() # 'chemin Catherine Legendre\n33959 LerouxBourg'
fake.department_name() # 'Tarn'

La bibliothèque est disponible en python 2.7 et 3, sous licence MIT, et est activement maintenue.

La liste de champs possibles (nom, prénom, adresse, siren, etc.) est presqu’aussi longue qu’une boucle sans condition de fin. Certains champs ne sont pas encore traduits mais ils sont assez secondaires.

Catégories :Python

Et pour quelques elif de plus

Face à une succession de if-elif-elif-elif-… longue comme un générique de film, il peut être tentant de les remplacer par un switch-case comme on peut en voir en C, PHP, Java, etc. C’est simplement impossible en Python car cette instruction n’existe pas.

Plusieurs propositions pour l’inclure dans le langage ont été faites mais elles ont été rejetées car Guido Van Rossum ne les a pas trouvées convaincantes. Son argumentaire détaillé est disponible dans la PEP 3103 publiée en 2006.

L’enchaînement de conditions que l’on souhaite éviter :

if personnage == "Le manchot":
    acteur = "Clint Eastwood"
elif personnage == "Colonel Douglas Mortimer":
    acteur = "Lee Van Cleef"
elif personnage == "El Indio":
    acteur = "Gian Maria Volontè"
#je vous fais grâce de toute la distribution
else:
    acteur = None

La FAQ de la documentation python explique comment se passer du switch (pour python 2, pour python 3). Inutile le lire les deux, c’est la même réponse à chaque fois. Elle recommande l’utilisation d’un dictionnaire. C’est aussi la solution la plus courante que l’on trouve dans du code Python.

L’équivalent du code précédent avec un dictionnaire est :

d = {"Le manchot": "Clint Eastwood", 
     "Colonel Douglas Mortimer": "Lee Van Cleef",
     "El Indio": "Gian Maria Volontè"}

acteur = d.get("El Indio", None)

Comme la structure switch n’est pas disponible dans le langage, plusieurs tentatives ont été faites pour l’ajouter à partir des structures existantes. Comme on peut s’y attendre, le code est plus verbeux et, à mon avis, la solution à base de dictionnaire reste préférable. Parmi les essais disponibles dans pypi.python.org, on peut trouver plusieurs choix d’interface :

Switchcase qui utilise l’instruction break de manière similaire au switch des autres langages : sans l’instruction break, les cas suivants sont automatiquement considérés comme vrai. Fonctionnalité permettant des comportments astucieuses mais qui est aussi un nid à bogues difficile à comprendre. C’est donc probablement une excellente idée de l’avoir ajoutée.

Switch qui a une interface me semblant plus simple à comprendre. La simulation du break est fait par un paramètre facultatif, ce qui s’écarte du switch canonique.

Pyswitch qui utilise les annotations pour définir les différents cas. Je ne suis pas sûr que ce soit le plus lisible mais c’est un choix original.

Une autre solution est de transformer le code en remplaçant les tests par du polymorphisme. Avec l’exemple précédent, on aurait :

class Personnage:
    acteur = None


class LeManchot(Personnage):
    acteur = "Clint Eastwood"


class ColonelDouglasMortimer(Personnage): 
    acteur = "Lee Van Cleef"


class ElIndio(Personnage):
    acteur = "Gian Maria Volontè"

p.acteur # p étant une instance d'une des classes précédente

Il n’est pas nécessaire que p soit une instance héritant de Personnage. Il suffit que l’interface soit identique. Cette transformation n’est pas spécifique à Python, elle est utilisable dans tous les langages objet.

Catégories :Python

Redimensionner des images avec python-resize-image

python-resize-image est une bibliothèque python disponible sur pypi.python.org. C’est une surcouche à PIL qui simplifie son usage pour des redimensionnements courants d’image. Bien pratique, elle ne remplacera pas PIL pour toutes les autres possibilités comme les empilements d’image par exemple.

La bibliothèque est utilisable avec python2 et python3 mais le fichier wheel n’a été fait que pour la version 2.
Pour l’installer sur python2, pip install python-resize-image suffit.
Pour l’installer sur python3, il faut d’abord cloner le projet, créer l’archive avant de l’installer dans son virtualenv.

Cet article montre un rendu d’exemple pour chaque fonction.

L’image de départ fait 1000 × 413 pixels :

Le fichier d'origine avant modification

Crop : resize_crop()

Dimension d’arrivée : 800 x 700 pixels

L’image d’arrivée possède une zone transparente au-dessus et au-dessous de la photo.

Avec la fonction resize_crop()

Cover : resize_cover()

Dimension d’arrivée : 800 x 700 pixels

Contrairement au crop, l’image d’arrivée est complètement remplie par la photo d’origine

Avec la fonction resize_cover()

Contain : resize_contain()

Dimension d’arrivée : 800 x 700 pixels

Comme le crop, l’image d’arrivée possède une zone transparente au-dessus et au-dessous de la photo.

Avec la fonction resize_contain()

Largeur fixée : resize_width()

Dimension d’arrivée : 400 x 165 pixels

Pas de transparence. L’image d’arrivée est un redimensionnement de l’image de départ, sous la contrainte de la largeur donnée en paramètre de resize_with().

Avec la fonction resize_width()

Hauteur fixée : resize_height()

Dimension d’arrivée : 969 x 400 pixels

Le comportement est équivalent à resize_width(), mais pour la hauteur.

Avec la fonction resize_height()

Vignette : resize_thumbnail()

Dimension d’arrivée : 800 x 330 pixels

La transformation respecte le ratio de départ et redimensionne au mieux pour respecter les dimensions passées en paramètre. Dans l’exemple, les 800 pixels pour la largeur sont bien là mais les 700 pixels demandés pour la hauteur ont été réduits à 330.

Avec la fonction resize_thumbnail()

Sources

J’ai pris la photo au Cap (en Afrique du Sud, pas en France) la semaine précédent la DebConf 16.

L’ensemble des images sont disponibles à http://stephane.yaal.fr/python-resize-image/ au cas où l’envoi sur WordPress ait modifié les données.

Le code source qui a permis de produire les images :

from PIL import Image
from resizeimage import resizeimage

with open('origine_1000.png', 'r') as f:
    TAILLE = [800, 700]
    FIXE = 400
    img = Image.open(f)
    img = resizeimage.resize_crop(img, TAILLE)
    img.save('crop.png', img.format)
    
    img = Image.open(f)
    img = resizeimage.resize_cover(img, TAILLE)
    img.save('cover.png', img.format)
    
    img = Image.open(f)
    img = resizeimage.resize_contain(img, TAILLE)
    img.save('contain.png', img.format)
    
    img = Image.open(f)
    img = resizeimage.resize_width(img, FIXE)
    img.save('width.png', img.format)
    
    img = Image.open(f)
    img = resizeimage.resize_height(img, FIXE)
    img.save('height.png', img.format)
    
    img = Image.open(f)
    img = resizeimage.resize_thumbnail(img, TAILLE)
    img.save('thumbnail.png', img.format)

Catégories :Python

Des graphiques à la Xkcd

26 février 2016 2 commentaires

Ou comment faire des graphiques dans le légendaire style de XKCD (une finesse du trait plus tranchante que Michel-Ange, des couleurs plus contrastées que Léonard de Vinci).

graphique à la xkcd

Les développeurs de Matplotlib l’ont fait et intégré à la bibliothèque. Globalement, il suffit d’initialiser le script python avec la fonction xkcd(). Cette fonction initialise des paramètres pour le rendu des graphiques.

with plt.xkcd():
    #le code pour produire le graphique

Installation

Dans Debian, le paquet de base de Matplotlib est python-matplotlib pour python2 et python3-matplotlib pour python3.

Pour que le graphique ait une police similaire à ceux de xkcd, la police Humor Sans doit être installée. Dans Debian, elle se trouve dans le paquet fonts-humor-sans.

Il est possible d’avoir encore une erreur signalant que la police n’est pas trouvée :

/usr/lib/python2.7/dist-packages/matplotlib/font_manager.py:1288: UserWarning: findfont: Font family [u'Humor-Sans'] not found. Falling back to Bitstream Vera Sans

En réalité, elle est bien accessible par matplotlib mais la bibliothèque a construit un cache des polices disponibles lors de la création d’un autre graphique par le passé. Ce cache n’est pas vidé après l’installation de la police. L’erreur survient car matplotlib regarde dans son cache et non les polices actuellement installées sur le système. Il suffit donc de supprimer ce cache (fontList.cache pour python2 ou fontList.py3k.cache pour python3) et d’exécuter à nouveau le script.


stephane@foehn:~$ ls .cache/matplotlib/
fontList.cache fontList.py3k.cache tex.cache
stephane@foehn:~$ rm .cache/matplotlib/fontList.cache #si script lancé avec python2

Et là, ça marche ! 🙂

Évitez tout de même de mettre des accents, la police ne dispose pas de ces caractères et un « ? » est affiché à la place.

Versions des logiciels utilisés, code source

paquet python-matplotlib : 1.5.1-1
paquet fonts-humor-sans : 1.0-1

Le code source qui a permis de produire le magnifique graphique inséré au début de l’article :

from matplotlib import pyplot as plt
import numpy as np

with plt.xkcd():
    fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1)
    ax.spines['right'].set_color('none')
    ax.spines['top'].set_color('none')
    plt.xticks([])
    plt.yticks([])
    ax.set_ylim([-1, 2])

    x = np.linspace(0, 10)
    plt.plot(x, np.sin(x) + 0.3, '--')

    plt.xlabel('abscisses')
    plt.ylabel('ordonnees')
    plt.title("c'est le plus beau graphique du monde !")

    plt.savefig("/tmp/graph_xkcd.png")
Catégories :Debian, Python

hglib, la bibliothèque python pour s’interfacer avec Mercurial

Mercurial, un système de gestion de version décentralisé, propose hglib pour pouvoir agir sur un dépôt programmatiquement depuis Python.

Hglib est compatible avec python 2 et 3 donc, à moins de faire de l’archéologie logicielle (python 2.3 et encore plus vieux), il n’y a pas de raison de s’en priver.

L’installation est triviale puisque la bibliothèque est disponible à partir de pypi (donc utilisable dans un virtualenv) ou dans votre distribution préférée (paquet python-hglib ou python3-hglib avec Debian).

Créer un dépôt

>>> import hglib
>>> depot = hglib.init(dest="/tmp/geographie")
>>> print(depot)
<hglib.client.hgclient object at 0x7f9e75dc2250>

Le résultat est équivalent à la commande :

$ hg init /tmp/geographie

Le résultat est banalement prévisible :

$ ls -la /tmp/geographie/
total 268
drwxr-xr-x  3 alice    alice     4096 oct.  13 21:07 .
drwxrwxrwt 17 root     root    262144 oct.  13 21:08 ..
drwxr-xr-x  3 alice    alice     4096 oct.  13 21:07 .hg

Accéder au dépôt

Pour ouvrir le dépôt :

>>> import hglib
>>> depot = hglib.open("/tmp/geographie")
>>> print(depot)
<hglib.client.hgclient object at 0x7f2b230a8050>

Il est possible de voir et ajouter des branches :

>>> depot.branches()
[]  #pas de branche particulière
>>> depot.branch()
'default' #'default' est la branche que Mercurial crée par défaut
>>> depot.branch("dev") #création et passage sur la branche 'dev'
'dev'
>>> depot.branch()
'dev'
>>> depot.branches()
[]
>>> depot.commit("ajout de la branche dev")
(0, 'f1c9646830e4efc57830ddd588001917c3046e5d')
>>> depot.branches()
[('dev', 0, 'f1c9646830e4')] #la branche 'dev' est maintenant visible

depot.commit() retourne un couple (numéro de révision, identifiant du nœud).
depot.branches() retourne la liste des branches, chacune avec le triplet (nom de la branche, numéro de révision, identifiant du nœud).

Ajouter des fichiers

Supposons que nous ayons un fichier listant les villes de France nommé villes.tsv et que nous décidions de l’ajouter à notre dépôt :

>>> depot.add("/tmp/geographie/villes.tsv") #équivaut à hg add /tmp/geographie/villes.tsv
True
>>> depot.status() #équivaut à hg status
[('A', 'villes.tsv')]
>>> depot.commit("ajout des villes") #équivaut à hg commit -m"ajout des villes"
(1, '815765cc96bde95ff8ca02305f798e1effc31688')
>>> depot.status()
[]

depot.status() retourne une liste des changements. Chaque changement est représenté par un couple (lettre_code, fichier). lettre_code vaut les valeurs classiques :

  • M : fichier modifié
  • A : fichier ajouté
  • R : fichier supprimé
  • etc.

Modifier des fichiers

Malheureusement, une erreur s’était glissée dans le fichier villes.tsv… Après la correction mais avant le commit :

>>> depot.diff() #le diff brut de décoffrage
'diff -r 815765cc96bd villes.tsv\n--- a/villes.tsv\tTue Oct 13 21:49:41 2015 +0200\n+++ b/villes.tsv\tTue Oct 13 22:13:12 2015 +0200\n@@ -13492,7 +13492,7 @@\n giey-sur-aujon\tGiey-sur-Aujon\n giez\tGiez\n giffaumont-champaubert\tGiffaumont-Champaubert\n-png-sur-yvette\tPng-sur-Yvette\n+gif-sur-yvette\tGif-sur-Yvette\n gigean\tGigean\n gignac-34\tGignac\n gignac-46\tGignac\n'
>>> for ligne in depot.diff().split('\n'):) #équivaut à hg diff
...  print(ligne)
... 
diff -r 815765cc96bd villes.tsv
--- a/villes.tsv	Tue Oct 13 21:49:41 2015 +0200
+++ b/villes.tsv	Tue Oct 13 22:13:37 2015 +0200
@@ -13492,7 +13492,7 @@
 giey-sur-aujon	Giey-sur-Aujon
 giez	Giez
 giffaumont-champaubert	Giffaumont-Champaubert
-png-sur-yvette	Png-sur-Yvette
+gif-sur-yvette	Gif-sur-Yvette
 gigean	Gigean
 gignac-34	Gignac
 gignac-46	Gignac

Après le commit, le diff est aussi vide qu’on peut s’y attendre :

>>> depot.commit("correction gif-sur-yvette")
(2, 'a972c3bee591b2ec549420ef6fbe36e2cc433189')
>>> depot.diff()
''

depot.diff() accepte un nom de fichier ou une plage de révisions en paramètre pour limiter les résultats.

Voir l’historique

L’équivalent de hg log est aussi disponible :

>>> import pprint # c'est juste pour la lisibilité
>>> pprint.pprint(depot.log())
[('2',
  'a972c3bee591b2ec549420ef6fbe36e2cc433189',
  'tip',
  'dev',
  'Alice <[email protected]>',
  'correction gif-sur-yvette',
  datetime.datetime(2015, 10, 13, 22, 21, 53)),
 ('1',
  '815765cc96bde95ff8ca02305f798e1effc31688',
  '',
  'dev',
  'Alice <[email protected]>',
  'ajout des villes',
  datetime.datetime(2015, 10, 13, 21, 49, 41)),
 ('0',
  'f1c9646830e4efc57830ddd588001917c3046e5d',
  '',
  'dev',
  'Alice <[email protected]>',
  'ajout de la branche dev',
  datetime.datetime(2015, 10, 13, 21, 18, 34))]

C’est une liste de commits. Chaque commit est un n-uplet (numéro de la révision, identifiant du nœud, tip ou pas, branche, auteur, message, date).

Créer une archive

Vu l’avancée pour l’humanité réalisée, il est temps de faire une archive du projet :

>>> depot.archive("/tmp/archive") # équivaut à hg archive /tmp/archive

Le répertoire obtenu :

$ ls -la /tmp/archive/
total 1200
drwxr-xr-x  2 alice    alice     4096 oct.  13 22:41 .
drwxrwxrwt 18 root     root    262144 oct.  13 22:41 ..
-rw-r--r--  1 alice    alice      168 oct.  13 22:41 .hg_archival.txt
-rw-r--r--  1 alice    alice   948448 oct.  13 22:41 villes.tsv

Générique de fin

Ce n’était qu’un survol des possiblités de hglib. De nombreuses possibilités n’ont pas été abordées (ouvrir un dépôt distant, depot.pull(), depot.push(), la création de .tar.gz pour les archives, etc.). La plupart des méthodes prennent des paramètres facultatifs pour en modifier le comportement.

Les cascades de cet article ont été réalisées avec Python 2.7.10, Mercurial 3.4.2 et hglib 1.7-1. Vous pouvez les reproduire chez vous, même si vous n’êtes pas un professionnel.

Aucun terminal n’a été maltraité lors de la réalisation de cet article.

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