PROJET AUTOBLOG


Sam et Max

source: Sam et Max

⇐ retour index

5 choses à apprendre en priorité en Python

dimanche 22 décembre 2013 à 09:57

Quand on apprend un nouveau langage de programmation, on apprend d’abord les bases. Et pour la plupart des langages, elles sont communes : déclarer une variable, faire des conditions et des boucles, faire des fonctions, importer un code d’un autre fichier, etc.

Ce qui va différencier le moment où vous savez programmer dans CE langage, ce sont des notions qui lui sont spécifiques et que vous commencez à maitriser.

Voici 5 notions spécifiques au langage qu’il faut apprendre en priorité si vous voulez pouvoir dire “je code en Python” :

Pip

Pip est la moyen le plus utilisé d’installer une bibliothèque externe dans l’environnement Python. Dès qu’on veut faire un projet sérieux, on en a besoin. Tellement qu’il va en fait être inclus par défaut dans Python 3.4.

Lire l’article sur pip.

Virtualenv

Virtualenv permet d’isoler plusieurs installations de Python. A partir du moment où l’on travaille sur plusieurs projets en même temps, il devient vite indispensable. Mais personnelement, je l’utilise même quand je n’ai qu’un projet installé sur une machine car il me permet de le séparer du setup Python du système et d’utiliser des hooks.

Un outil qui a été ajouté dans la lib standard en Python 3.3. J’apprécie que le pragmatisme de l’évolution de Python qui intègre petit à petit les projets qui se sont révélés les outils de facto dans la communauté.

Lire l’article sur virtualenv.

Les listes en intention

J’ai envie de dire l’itération en générale, mais c’est un très vaste sujet, et il est couvert en grande partie par les 3 derniers points.

La liste en intention, ou liste en compréhension, est une manière de boucler sur un itérable (souvent une liste), avec optionellement un filtre, afin de produire une nouvelle liste. En une ligne.

C’est stylistiquement la marque de fabrique de Python (même si c’est piqué à Haskell). C’est également ce qui le rend aussi expressif. On peut presque coder tout un programme en déclaratif avec des enchainements de listes en intention.

C’est beau, propre, efficace et court. IN-DIS-PEN-SA-BLE.

Lire l’article sur les listes en intention.

L’unpacking

L’unpacking est une autre fonctionalité typiquement pythonienne qui permet de prendre un itérable (souvent un tuple), et de mettre ses éléments dans des variables d’une traite.

Cela permet d’augmenter drastiquement la lisibilité des programmes.

Lire les articles sur l’unpacking.

Les générateurs

Les générateurs permettent non seulement un énorme gain en performance, mais en plus ils autorisent le traitement itératif de flux de données dont on ne connait pas la taille en avance, voire de taille infinie. Si vous utilisez des expressions génératrices, vous pourrez le faire en déclaratif. Si vous utilisez yield, vous pourrez cacher un algorithme complet derrière une simple boucle for.

Lire l’article sur yield.

Le reste ?

Tout le reste, c’est du détail. Les décorateurs, la POO, l’opérateur with, les métaclasses, les astuces magiques pour faire ceci ou cela. C’est bien, mais ça peut attendre. Ce sont ces 5 notions, qui, bien utilisées, feront d’un programmeur un dev Python.

flattr this!

De l’intérêt des tuples comme clé de dictionnaire

samedi 21 décembre 2013 à 09:18

On peut utiliser n’importe quel objet hashable comme clé de dictionnaire en Python, pas uniquement des strings. Donc des entiers bien entendu, mais également, et c’est rarement utilisé, des tuples.

Imaginez que vous ayez une structures de données ainsi initialisée :

from random import choice, randint
 
tags = ('personne', 'animal', 'objet')
depart = {}
 
depart = {'%s_%s' % (choice(tags), randint(0, 10)): None for x in range(10)}

Cela donne quelque chose comme ça :

{u'personne_6': None,
 u'personne_5': None,
 u'objet_9': None,
 u'objet_6': None,
 u'objet_4': None,
 u'personne_8': None,
 u'objet_2': None,
 u'objet_0': None,
 u'animal_8': None}

On voit que les clés ont ici une valeur sémantique importante : elles sont porteuses de sens.

Si vous voulez la liste des nombres utilisés, il va vous falloir changer votre structure de données : en avoir plusieurs séparées, probablement. Ou alors faire de la manipulation de chaîne à base de split() et de casting.

Par contre, si vous utilisez un tuple comme clé, vous avez le même format pour votre dictionnaire depart, avec les mêmes possibilités, mais en plus un accès aux clés plus complet.

Déjà le code de génération est plus simple :

depart = {(choice(tags), randint(0, 10)): None for x in range(10)}

Ce qui donne :

{(u'animal', 2): None,
 (u'personne', 5): None,
 (u'personne', 4): None,
 (u'objet', 6): None,
 (u'objet', 10): None,
 (u'animal', 7): None,
 (u'animal', 1): None,
 (u'animal', 10): None,
 (u'personne', 8): None}

Mais en prime, on peut faire ça :

for (tag, number), value in depart.items():
    print tag, number, value
 
## animal 2 None
## personne 5 None
## personne 4 None
## objet 6 None
## objet 10 None
## animal 7 None
## animal 1 None
## animal 10 None
## personne 8 None

Bref, quand vos clés ont une valeur sémantique importante, pensez à utiliser des tuples, voir carrément, un namedtuple, qui est une structure de données trop souvent ignorée alors qu’elle est très puissante, et peut remplacer bien des classes conteneurs.

flattr this!

Changement dans l’unpacking des iterables en Python 3

vendredi 20 décembre 2013 à 08:49

Ahhh, l’unpacking… On croit qu’on a complètement fait le tour de cette fonctionalité merveilleuse, et PAF, on découvre encore autre chose.

Par exemple, la syntaxe a été améliorée avec Python 3, et accepte maintenant un unpacking partiel !

Ca se fait en l’utilisant l’opérateur splat, c’est à dire l’étoile :

>>> l = list(range(5))
>>> l
[0, 1, 2, 3, 4]
>>> a, *b = l
>>> a
0
>>> b
[1, 2, 3, 4]
>>> a, *b, c = l
>>> a
0
>>> b
[1, 2, 3]
>>> c
4

Ca marche bien entendu également dans les boucles for.

flattr this!

Article retiré pour cause de grosse merde

jeudi 19 décembre 2013 à 09:10

Désolé pour ceux qui ont reçu l’article via RSS ou email. Je le retire. C’était de la merde.

flattr this!

Solution de l’exercice d’hier

mardi 17 décembre 2013 à 10:17

Il faut bien noter que ce n’est qu’une solution parmi d’autres :

import re
import sys
import string
import unicodedata
 
mots = {}
texte = open(sys.argv[1]).read().decode('utf8').replace(u'œ', 'oe')
texte = unicodedata.normalize('NFKD', texte).encode('ascii', 'ignore')
texte = re.sub('[^%s]' % string.ascii_lowercase, ' ', texte.lower())
 
for i, e in enumerate(texte.split()):
    mots.setdefault(e, []).append(i)
 
mots = sorted(mots.items(), key=lambda x: (len(x[1]), sorted(x[1])))
 
for mot, positions in mots:
    print('- %s: %s' % (mot, ', '.join(map(str, positions))))

On ignore cordialement toute gestion d’erreur, donc le code peut se permettre d’être court. Et .replace(u'œ', 'oe') n’est pas très générique :-)

Dans les propositions de code des commentaires, il faut noter :

Décorticage :

import re
import sys
import string
import unicodedata
 
# On va tocker les mots dans ce dico
mots = {}
 
# Je récupère en vrac le contenu du fichier. Comme on a pas de gestion des
# erreurs, je récupère cash pistache le chemin de la ligne de commande
# et je suppose un encoding en UTF8. Le résultat obtenu est un objet
# unicode de tout le texte du fichier, sans le caractère 'œ'.
texte = open(sys.argv[1]).read().decode('utf8').replace(u'œ', 'oe')
 
# Astuce pour normaliser les caractères spéciaux. Ne marche que pour 
# l'alphabet latin malheureusement. Donc le script est limité. Unidecode
# permettrait d'avoir un script plus générique.
texte = unicodedata.normalize('NFKD', texte).encode('ascii', 'ignore')
 
# string.ascii_lowercase contient toutes les lettres ASCII en minuscule,
# ce qui permet de faire un remplacement, via regex, de 
# [^abcdefghijklmnopqrstuvwxyz]', c'est à dire tout ce qui n'est pas
# une lettre ASCII minuscule.
texte = re.sub('[^%s]' % string.ascii_lowercase, ' ', texte.lower())
 
# Je récupère tous les "mots", split() sans paramètre coupe en effet toute 
# combinaison de caractères non imprimables. enumerate() me permet d'avoir
# la position de chaque mot. setdefault() me permet d'ignorer les clés qui
# n'existent pas encore dans le dico. J'aurais pu utiliser un defaultdict, mais
# comme on a qu'une seule ligne ici, c'est plus court.
# J'obtiens donc un dico {mot1: [positon1, position2, ...], mot2: ...}
for i, e in enumerate(texte.split()):
    mots.setdefault(e, []).append(i)
 
# On récupère le contenu du dico sous forme de liste de tuples 
# [(mot, positions)...], et on l'ordonne selon le nombre d'apparitions
# (len(x[1])), ou a défaut par ordre naturel des apparitions sorted(x[1]).
# Pour rappel, key attend une fonction qui prend chaque élement, et retourne
# une clé. La clé est utilisée pour ordonner les éléments : chaque élément
# voit sa clé comparée à celle des autres, et ordonnée par ordre naturel.
# Y a un article sur ça : http://sametmax.com/ordonner-en-python/
# En gros, une entrée ('salut', 4, 18) aura pour clé (2, (4, 18)),
# ce que Python peut comparer facilement.
# Je réalise en rédigeant ces lignes que mon sorted est inutile, puisque 
# le processus est incrémental et déjà ordonné. Je le laisse comme référence.
mots = sorted(mots.items(), key=lambda x: (len(x[1]), sorted(x[1])))
 
# Et on affiche tout ça, non sans caster les positions du type int vers str
# pour éviter un crash
for mot, positions in mots:
    print('- %s: %s' % (mot, ', '.join(map(str, positions))))

Enoncé de l’exercice.

Télécharger le code de l’article.

flattr this!

I'm richer than you! infinity loop