Site original : Sam & Max: Python, Django, Git et du cul
Suite aux commentaires, j’ai fais une refonte des dispos :
Histoire d’éviter d’éparpiller des versions partout, je l’ai juste réup au même endroit.
Merci, donc, pour toutes les remarques qui ont significativement permises d’améliorer la prez.
Parcourez votre itérable, passez un callable, retournez un indexable…
En Python on aime le duck typing. On ne va donc pas s’intéresser à un type, mais à un comportement.
Quand vous voyez un suffixe “-able” en anglais, ça veut dire “accepte qu’on lui fasse quelque chose”. Par exemple, “fuckable” = “baisable”.
Sur ce morceau de poésie, je vous offre un peu de Vivaldi pour faire glisser profondément ce gros article qui va lister les chosables les plus connus.
Le plus important en Python.
Un itérable est ce qui accepte l’itération, ce sur quoi on peut itérer, c’est à dire une collection dont on peut prendre les éléments un à un.
Pour faire simple, tout ce sur quoi on peut appliquer une boucle for
.
L’exemple le plus connu sont les listes :
for x in ['une', 'liste']: print(x) une liste |
Mais cela s’applique à bien d’autres types :
for x in 'unechaine': ... print(x) ... u n e c h a i n e for x in ('un', 'tuple'): ... print(x) ... un tuple for x in {'un': 'dico', 'par': 'cle'}: ... print(x) ... un par for x in set(('un', 'set')): ... print(x) ... un set with open('/tmp/test', 'w') as f: f.write('un\nfichier') ... for x in open('/tmp/test'): ... print(x, end="") ... un fichier |
Les tuples, dicos, sets, fichiers, et strings sont itérables. Beaucoup de structures de données du module collections (deque
, namedtupple
, defaultdict
) sont itérables.
Mais surtout, les générateurs sont itérables :
def generator(): yield 1 yield 2 for x in generator(): print(x) 1 2 |
Pour vérifier si quelque chose est itérable, on peut utiliser la fonction iter()
. Cette fonction prend un iteérable, et retourne un générateur (appelé “iterator”) qui permet d’énumérer chaque élément de l’iterable :
lst = ['ceci', 'est', 'aussi', 'une', 'liste'] generateur = iter(lst) next(generateur) 'ceci' next(generateur) 'est' next(generateur) 'aussi' next(generateur) 'une' |
iter()
lève TypeError
sur un non iterable :
iter(1) Traceback (most recent call last): File "", line 1, in iter(1) TypeError: 'int' object is not iterable |
Pour la culture, c’est ainsi que la boucle for
fonctionne : à coup de next()
sur un itérateur.
On peut rendre n’importe quelle objet itérable en définissant la méthode __iter__
, qui doit retourner un générateur :
class NouvelIterable: def __iter__(self): # mettre des yield marche aussi return iter([1, 2, 3]) for x in NouvelIterable(): print(x) 1 2 3 |
Les itérables sont les bidulables les plus important en Python car de très nombreuses fonctions les acceptent :
list(sorted(('AZERTY'))) # tri ['A', 'E', 'R', 'T', 'Y', 'Z'] list(reversed('AZERTY')) # inversion ['Y', 'T', 'R', 'E', 'Z', 'A'] list(zip('AZERTY', (100, 300, 600))) # lier deux itérables [('A', 100), ('Z', 300), ('E', 600)] any(set((1, 0, 1, 0, 1, 1, 2))) # un élément au moins est vrai ? True all(set((1, 0, 1, 0, 1, 1, 2)) # tous les éléments sont vrais ? ) False |
Et elles retournent souvent des itérables également :)
La plupart des itérables sont compatibles entre eux. Y compris les générateurs. Qui souvent traitent eux même des itérables et retournent des itérables. Cela permet de faire d’énormes pipelines de traitements connectés les uns aux autres :
s = '123456789' res = (int(x) * x for x in s) tuple(reversed(list(res)))[:4] ('999999999', '88888888', '7777777', '666666') |
Dont on peut changer la valeur.
Quand on assigne une variable en Python, on ne change pas la valeur de l’objet, on change la valeur de la variable :
a = 1 a = 2 |
Ici 1
n’a pas changé, la valeur stockée dans a
a changé.
C’est différent de ceci :
a = [1] a[0] = 2 a [2] |
Ici, c’est la même liste qui est dans a
, mais la valeur stockée dans la liste a changé.
Cette notion est importante car en Python, les variables ne contiennent en fait pas vraiment des valeurs, mais des références à ces valeurs.
Si je change le contenu de la variable, il n’y a pas d’effet de bords :
a = [1, 2, 3] b = a # b et a contienne une référence à la même liste b [1, 2, 3] a = [4, 5, 6] # le contenu de a change b [1, 2, 3] # b et a ont une contenu différents |
Si je change la valeur de ma structure de données, ici ma liste, alors il y a un effet de bord :
a = [1, 2, 3] b = a a[0] = 1000 b # a et b référencent la même liste [1000, 2, 3] |
En effet, a
ne contient pas la liste, mais une référence à la liste. Quand on copie le contenu de a
vers b
, on ne copie pas la liste, mais cette référence. Donc a
et b
sont des variables qui pointent vers la même liste.
Il est alors important de savoir quelles opérations modifient quelque chose, et lesquelles ne les modifient pas.
Les listes, les dictionnaires et les sets sont modifiables, on dit qu’il sont “mutables”.
On peut le voir avec la fonction id()
qui renvoie le numéro unique de l’objet :
une_liste = [] id(une_liste) 140693805855368 une_liste.append(1) une_liste [1] id(une_liste) 140693805855368 |
La liste a changé, mais l’id est le même, c’est le même objet.
Les opérations qui “changent la valeur” sur les types mutables sont performantes en Python car il n’y a pas besoin de recréer un objet à chaque fois.
Les tuples, les nombres, les chaînes de caractères ne sont pas modifiables. Il ne sont pas “mutables” :
id(une_liste) 140693805855368 un_tuple = (1, 2, 3) id(un_tuple) 140693772746040 un_tuple += (4, 5, 6) un_tuple (1, 2, 3, 4, 5, 6) id(un_tuple) 140693772879144 |
Le tuple n’a pas changé : l’id n’est pas le même car la variable un_tuple
contient un nouvel objet.
Les opérations qui “changent la valeur” sur les types non mutables sont moins performantes en Python car il faut recréer un objet à chaque fois.
Par défaut, toute classe que vous écrivez crée un objet mutable.
Tout ce qui peut être appelé, c’est à dire qu’on peut mettre ()
après le nom de la variable qui le contient pour obtenir un effet.
Ce qui vient en premier en tête ce sont les fonctions (ou les méthodes):
def foo(): ... print("Je suis appelée") ... foo() # j'appelle ma fonction Je suis appelée |
Mais, en Python, le concept d’appeler va plus loin.
Une classe est un callable :
class Bar: def __init__(self): print("Je suis appelée") Bar() # j'instancie en utilisant () Je suis appelée |
Un type est un callable :
set() set() |
Et on peut rendre n’importe quel objet callable en définissant la méthode __call__
:
class UnCallableQuiCreerUnCallable: def __call__(self): print('Je suis appelé') callable = UnCallableQuiCreerUnCallable() callable() Je suis appelé |
Donc quand on vous dit : “ceci attend un callable en paramètre”, vous pouvez passer n’importe quel type de callable. Pas juste une fonction. On peut créer des décorateurs avec et pour n’importe quel callable.
Si on essaye d’appeler un objet qui n’est pas un callabe, on obtient un TypeError
:
lst [1] lst() Traceback (most recent call last): File "", line 1, in lst() TypeError: 'list' object is not callable |
Les clés des dictionnaires n’ont pas besoin d’être des chaînes de caractères. Elles peuvent être n’importe quel objet hashable. Pour les types de base, ce sont les non mutables, soit les strings, mais aussi ints, floats ou tuples :
dico = {('une', 'cle', 'qui', 'est', 'un', 'tuple'): 1} len(dico) 1 dico[('une', 'cle', 'qui', 'est', 'un', 'tuple')] 1 |
Pour obtenir cet effet, le dictionnaire prend l’objet passé en clé, et calcule un hash, une empreinte unique de l’objet. Pour que cela marche, il faut que le hash d’un objet donne toujours le même résultat si il est appliqué deux fois au même objet, ou à deux objets parfaitement égaux.
Un objet hashable est donc un objet qu’on peut utiliser comme clé de dictionnaire. C’est un objet qu’on peut passer à la fonction hash()
. Dans la stdlib, les types non mutables sont hashable, et les types mutables ne le sont pas :
hash("fdkslmf") 4874978338908949266 hash([]) Traceback (most recent call last): File "", line 1, in hash([]) TypeError: unhashable type: 'list' |
Mais on peut créer sa propre définition de ce qu’est un objet hashable avec la méthode __hash__
, qui doit retourner un entier :
class Personne: def __init__(self, nom, prenom, age): self.nom = nom self.prenom = prenom self.age = age def __hash__(self): return sum((ord(x) for x in (self.nom + self.prenom))) + self.age ... hash(Personne("bob", "sinclaire", 78)) 1339 |
Vous avez néanmoins intérêt à savoir ce que vous faites en faisant ça, c’est un nid de frelons.
Ce dont on peut récupérer une partie avec []
. Essayer sur un objet qui ne l’est pas peut lever TypeError: 'x' object is not subscriptable
ou une sous erreur.
Car on peut utiliser []
de deux façons.
Dont on peut récupérer un élément à une position particulière, avec la syntaxe []
. Dans la stdlib, les listes, les chaînes de caractères, les tuples et les dictionnaires sont indexables mais pas les sets :
"fdjskl"[0] 'f' ('1', '2')[0] '1' {'yo': 'man'}['yo'] 'man' s = set((1, 2)) s[0] Traceback (most recent call last): File "", line 1, in s[0] TypeError: 'set' object does not support indexing |
On peut définir son propre comportement d’indexation avec __getitem__
:
class MainGauche: def __getitem__(self, index): return "Index de la main gauche" main = MainGauche() print(main[0]) Index de la main gauche |
Dont on peut récupérer un sous ensemble des éléments avec la syntaxe [start:stop:step]
. Un sliceable est souvent indexable, mais l’inverse n’est pas forcément vrai. Dans la stdlib, les listes, les strings et les tuples sont sliceables, mais pas les dictionnaires ni les sets :
"fdjskl"[1::2] 'dsl' ('1', '2', True, False)[:-1] ('1', '2', True) {'yo': 'man'}[1:2] Traceback (most recent call last): File "", line 1, in {'yo': 'man'}[1:2] TypeError: unhashable type: 'slice' |
Le slice s’implémente comme l’index, avec __getitem__
. La différence est qu’au lieu de recevoir une valeur ordinaire, vous allez recevoir un objet slice :
class MainDroite: def __getitem__(self, slice): print(slice.start, slice.stop) return "Slice de la main droite. Heu..." main = MainDroite() print(main[2:6]) 2 6 Slice de la main droite. Heu... |
Bon, je vais rejoindre Max et j’ai 12 heures de vol et 13 heures d’escale, donc je ferai les fixes sur les slides durant le vol. Ca me donne plus de temps pour le reste. Pas d’amélioration aujourd’hui donc. Mais merci beaucoup à tous les retours, ils sont vraiment très utiles.
Un de ces 4 faudra que je fasse un article sur les tests ergonomiques, ça marche un peu pareil :)
Dans le cadre de mon travail sur WAMP, j’ai proposé à Tobias de commencer par une présentation générale de la stack techno sous forme de slide show.
L’idée est de mettre ça dans le header des sites de WAMP, crossbar.io et autobahn, afin que quand les gens arrivent dessus ils puissent rapidement voir de quoi on parle. Ou alors, si on est sur un forum, on peut linker vers les diapos pour donner un contexte.
Comme prévu, je fais une première version en français que je poste ici. Puis je vais récolter vos commentaires : qu’est-ce qu’on comprend pas, quelles informations manquent, qu’est-ce qui est flou, ambigüe, etc.
Ensuite je l’améliorerai lors de la traduction en anglais qui sera ensuite proposée à Tavendo.
Ces informations sont éparpillées sur le net, et même sur le blog. Mais c’est un peu le package que tout dev Python qui se lance doit apprendre petit à petit. Alors je vais vous épargner les recherches.
Ne prenez pas ça comme une checklist qu’il faut impérativement faire avant de programmer. Coder est le plus important pour apprendre. Tout le reste est facultatif. Mais cet article liste des raccourcis qui vous seront utiles au fur et à mesure de votre progression.
Sous Linux, avec votre gestionnaire de paquet. Mais souvenez-vous que la 2.7 est installée par défaut.
Sous Mac, avec homebrew. Mais souvenez-vous que la 2.7 est installée par défaut. Vous aurez besoin de GCC. Mais pas besoin de télécharger xcode pour ça, il y a des solutions légères.
Sous Windows, utilisez miniconda. L’installeur officiel est pas mal, mais miniconda vous aidera beaucoup pour plein de choses, comme installer facilement les extensions compilées type scipy ou QT. Et cochez toujours l’option pour ajouter Python au SYSTEM PATH, sinon il faudra le faire à la main et c’est relou.
Python 3 sauf si :
Dans tous les cas, souvenez-vous :
py
permet de choisir quel Python lancer sous Windows (py -2
ou py -3
). Sous linux, on peut carrément faire python2
ou python3
, ou python3.4
.-p.
Y a combien de Python bordel ?
Pour vous, un seul : CPython. C’est-à-dire ce que les gens appellent “Python”.
Vous pouvez ignorer le reste. Vous n’avez pas à vous inquiéter de devoir utiliser un autre un jour, devoir passer à un autre, etc. Ce sont juste des bonus qui peuvent aider des gens dans des cas particuliers, rien d’indispensable.
pip. Installez toujours pip. Utilisez toujours pip. Apprenez à bien utiliser pip avec toutes ses options.
Et souvenez-vous qu’on peut installer un module pour une version de python en particulier en faisant pythonX -m pip
ou sous windows, py -x -m pip
.
Une exception pour les extensions :
Si vous êtes sous Windows, installer en plus miniconda va vous simplifier énormément la vie pour installer les extensions compilées comme scipy.
Et si vous êtes sous Linux utilisez votre gestionnaire de paquet sans quoi vous allez devoir installer gcc et les headers de Python (package python-dev sous Ubuntu par exemple).
Setuptools. Le reste a été fusionné ou déprécié.
64 bits. Si vous avez un jour une erreur indébuggable sous windows, testez avec une version 32 bits au cas où. Mais 64 bits reste le choix par défaut.
Aucun IDE n’est nécessaire en Python. On peut coder avec un simple éditeur de texte et la console. Je fais toutes mes formations avec Notepad++ sous Windows, ou Gedit sous Ubuntu.
Vous pouvez donc juste utiliser ce que vous aimez le plus.
Tkinter si c’est une UI simple (quelques fenêtres, quelques formulaires). WxPython si c’est un programme complexe en Python 2.7. C’est le meilleur équilibre poids/puissance. Kivy si ça doit tourner sur mobile.
PyQt/PySide sont les plus puissants, mais beaucoup plus dur à utiliser. Ca vaut rarement le coup, sauf que pour le moment, c’est la seule solution solide en Python 3.
On peut coder avec Python sans tout ça, mais la vie est plus belle avec.
Si vous êtes sous windows, souvenez-vous que Shift avant un clic droit ajoute des options supplémentaires au menu contextuel de votre explorateur de fichiers. Avec notamment “Ouvrir une invite de commande dans ce dossier”. Sous Mac il y a un réglage pour ça. Sous Ubuntu il y a un plugin.
Ah oui, et sous windows, utilisez une console décente.
Réglez votre éditeur de texte pour utiliser 4 espaces pour indenter. Si vous préférez les tabs, réglez aussi votre éditeur de texte pour utiliser 4 espaces pour indenter.
Tant qu’on y est, apprenez à gérer votre encoding. Particulièrement, assurez-vous de savoir où changer l’encoding des caractères de vos fichiers dans les options de votre éditeur de texte. Et mettez le par défaut sur UTF8. Utilisez toujours des chaînes de caractères unicode et le header # -*- coding: utf-8 -*-
.
Utilisez print
avec la syntaxe sous forme de fonction. Et faites des imports absolus, jamais relatifs.
En fait, si vous êtes en Python 2.7, activez le plus de comportement de Python 3 possible en faisant les imports de __future__
.
Pour un débutant, voici donc un bon template de fichier Python 2.7 par défaut :
# -*- coding: utf-8 -*- """ Documenter le module ici. """ from __future__ import (unicode_literals, absolute_import, print_function, division) def main(): """ Run the whole program """ # votre code ici print('Hello !') if __name__ == '__main__': main() |
/
devient la division classique, //
la division entière.Pour Python 3, c’est plus simple :
""" Documenter le module ici. """ def main(): """ Run the whole program """ print('Hello !') if __name__ == '__main__': main() |
Utilisez toujours des dates en UTC. Ne convertissez qu’à l’affichage. Donc ça veut dire utiliser datetime.utcnow()
et non datetime.now()
Les modules disponibles avec Python sont ce qu’on appelle la bibliothèque standard, ou stdlib. Il y a beaucoup de modules très utiles.
Néanmoins certains doivent avoir la priorité dans votre apprentissage. Les plus importants sont :
Quelques modules sont vieux, et ça se sent dans leur facilité d’usage. Pour cette raison, si vous utilisez beaucoup certains d’entre eux, des alternatives installables avec pip peuvent vous simplifier énormément la vie :
Stdlib | Lib tierce partie plus pratique> |
datetime | arrow |
os, sys, shutil | path.py |
argparse | docopt |
unittest | pytest. En fait ne faites jamais de tests unitaires sans pytest. La vie est trop courte. |
urllib2 | requests |
Les trucs importants à apprendre en Python (qui sont typiques de ce langage, j’entends) avant de se lancer dans la sophistication :
Avec ça vous pouvez aborder sereinement tout problème. Le reste, c’est du bonus.
Typiquement, on peut repousser à bien plus tard l’apprentissage de :