python 3 – Sam & Max http://sametmax.com Du code, du cul Wed, 30 Oct 2019 15:34:04 +0000 en-US hourly 1 https://wordpress.org/?v=4.9.7 32490438 Tout ce qui fait que Python 3 est meilleur que Python 2 http://sametmax.com/tout-ce-qui-fait-que-python-3-est-meilleur-que-python-2/ http://sametmax.com/tout-ce-qui-fait-que-python-3-est-meilleur-que-python-2/#comments Thu, 26 Jan 2017 14:17:40 +0000 http://sametmax.com/?p=22144 Avec de nombreuses distros Linux qui viennent avec Python 3 par défaut ainsi que Django et Pyramid qui annoncent bientôt ne plus supporter Python 2, il est temps de faire un point.

Python 3 est aujourd’hui majoritairement utilisé pour tout nouveau projet ou formation que j’ai pu rencontrer. Les plus importantes dépendances ont été portées ou possèdent une alternative. Six et Python-future permettent d’écrire facilement un code compatible avec les deux versions dans le pire des cas.

Nous sommes donc bien arrivés à destination. Il reste quelques bases de code encore coincées, la vie est injuste, mais globalement on est enfin au bout de la migration. Mais ça en a mis du temps !

Il y a de nombreuses raisons qui ont conduit à la lenteur de la migration de la communauté :

  • Python 2 est un très bon langage. On ne répare pas ce qui marche.
  • Il y a beaucoup de code legacy en Python 2 et ça coûte cher de migrer.
  • La PSF a été trop gentille avec la communauté et l’a chouchoutée. En JS et Ruby ils ont dit “migrez ou allez vous faire foutre” et tout le monde a migré très vite.

Mais je pense que la raison principale c’est le manque de motivation pour le faire. Il n’y a pas un gros sticker jaune fluo d’un truc genre “gagnez 30% de perfs en plus” que les devs adorent même si ça n’influence pas tant leur vie que ça. Mais les codeurs ne sont pas rationnels contrairement à ce qu’ils disent. Ils aiment les one-liners, c’est pour dire.

Pourtant, il y a des tas de choses excellentes en Python 3. Simplement:

  • Elles ne sont pas sexy.
  • Elles ne se voient pas instantanément.
  • Personne n’en parle.

Après 2 ans de Python 3 quasi-fulltime, je peux vous le dire, je ne veux plus coder en Python 2. Et on va voir pourquoi.

Unicode, mon bel unicode

On a crié que c’était la raison principale. A mon avis c’est une erreur. Peu de gens peuvent vraiment voir ce que ça implique.

Mais en tant que formateur, voilà ce que je n’ai plus a expliquer:

  • Pourquoi un accent dans un commentaire fait planter le programme. Et # coding:
  • Pourquoi il faut faire from codecs import open et non pas juste open.
  • Pourquoirequest.get().read() + 'é' fait planter le programme. Et encode() et decode().
  • Pourquoi os.listdir()[0] + 'é' fait afficher des trucs chelous.
  • Pourquoi print(sql_row[0]) fait planter le programme ou affiche des trucs chelous. Ou les deux.

Et je peux supprimer de chacun de mes fichiers:

  • Tous les u ou les from __future__/codecs
  • Les en-tête d’encoding.
  • La moitié des encode/decode.

Et toutes les APIS ont un paramètre encoding, qui a pour valeur par défaut ‘UTF8’.

Debuggage for the win

Des centaines d’ajustements ont été faits pour faciliter la gestion des erreurs et le debuggage. Meilleures exceptions, plus de vérifications, meilleurs messages d’erreur, etc.

Quelques exemples…

En Python 2:

>>> [1, 2, 3] < "abc"
True

En Python 3 TypeError: unorderable types: list() < str() of course.

En Python 2, IOError pour tout:

>>> open('/etc/postgresql/9.5/main/pg_hba.conf')
Traceback (most recent call last):
  File "", line 1, in 
IOError: [Errno 13] Permission denied: '/etc/postgresql/9.5/main/pg_hba.conf'
>>> open('/etc/postgresql/9.5/main/')
Traceback (most recent call last):
  File "", line 1, in 
IOError: [Errno 21] Is a directory: '/etc/postgresql/9.5/main/'

En Python 3, c'est bien plus facile à gérer dans un try/except ou à debugger:

>>> open('/etc/postgresql/9.5/main/pg_hba.conf')
Traceback (most recent call last):
  File "", line 1, in 
PermissionError: [Errno 13] Permission denied: '/etc/postgresql/9.5/main/pg_hba.conf'
>>> open('/etc/postgresql/9.5/main/')
Traceback (most recent call last):
  File "", line 1, in 
IsADirectoryError: [Errno 21] Is a directory: '/etc/postgresql/9.5/main/'

Les cascades d'exceptions en Python 3 sont très claires:

>>> try:
...     open('/') # Erreur, c'est un dossier:
... except IOError:
...     1 / 0 # oui c'est stupide, c'est pour l'exemple
... 
Traceback (most recent call last):
  File "", line 2, in 
IsADirectoryError: [Errno 21] Is a directory: '/' 

During handling of the above exception, another exception occurred:

  File "", line 4, in 
ZeroDivisionError: division by zero

La même chose en Python 2:

Traceback (most recent call last):
  File "", line 4, in 
ZeroDivisionError: integer division or modulo by zero

Bonne chance pour trouver l'erreur originale.

Plein de duplications ont été retirées.

En Python 2, un dev doit savoir la différence entre:

  • range() et xrange()
  • map/filter et itertools.imap/itertools.ifilter
  • dict.items/keys/values, dict.iteritems/keys/values et dict.viewitems/keys/values
  • open et codecs.open
  • Et md5 vs hashlib.md5
  • getopt, optparse, argparse
  • Cette manipulation de fichier se fait avec sys, os ou shutil ?
  • On hérite de UserDict ou dict ? UserList ou list ?
  • ...

Sous peine de bugs ou de tuer ses perfs.

Certains bugs sont inévitables, et les modules csv et re sont par exemple pour toujours buggés en Python 2.

Good bye boiler plate

Faire un programme correct en Python 2 requière plus de code. Prenons l'exemple d'une suite de fichier qui contient des valeurs à récupérer sur chaque ligne, ou des commentaires (avec potentiellement des accents). Les fichiers font quelques centaines de méga, et je veux itérer sur leurs contenus.

Python 2:

# coding: utf8

from __future__ import unicode_literals, division

import os
import sys

from math import log
from codecs import open
from glob import glob
from itertools import imap  # pour ne pas charger 300 Mo en mémoire d'un coup

FS_ENCODING = sys.getfilesystemencoding()

SIZE_SUFFIXES = ['bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']

def file_size(size):
    order = int(log(size, 2) / 10) if size else 0  # potential bug with log2
    size = size / (1 << (order * 10))
    return '{:.4g} {}'.format(size, suffixes[order])

def get_data(dir, *patterns, **kwargs):
    """ Charge les données des fichiers  """

    # keyword only args
    convert = kwargs.get('convert', int)
    encoding = kwargs.get('encoding', 'utf8')

    for p in patterns:
        for path in glob(os.path.join(dir, p)):
            if os.path.isfile(path):
                upath = path.decode(FS_ENCODING, error="replace")
                print 'Trouvé: ', upath, file_size(os.stat(path).st_size)

                with open(path, encoding=encoding, error="ignore") as f:
                    # retirer les commentaires
                    lines = (l for l in f if "#" not in l)
                    for value in imap(convert, f):
                        yield value

C'est déjà pas mal. On gère les caractères non ASCII dans les fichiers et le nom des fichiers, on affiche tout proprement sur le terminal, on itère en lazy pour ne pas saturer la RAM... Un code assez chouette, et pour obtenir ce résultat dans d'autres langages vous auriez plus dégueu (ou plus buggé).

Python 3:

# wow, so much space, much less import
from math import log2 # non buggy log2
from pathlib import Path # plus de os.path bullshit

SIZE_SUFFIXES = ['bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']

def file_size(size):
    order = int(log2(size) / 10) if size else 0   
    size = size / (1 << (order * 10))
    return f'{size:.4g} {suffixes[order]}' # fstring

def get_data(dir, *patterns, convert=int, encoding="utf8"): # keyword only
    """ Charge les données des fichiers  """

    for p in patterns:
        for path in Path(dir).glob(p):
            if path.is_file():
                print('Trouvé: ', path, file_size(path.stat().st_size))

                with open(path, encoding=encoding, error="ignore") as f:
                    # retirer les commentaires
                    lines = (l for l in f if "#" not in l)
                    yield from map(convert, lines) # déroulage automatique

Oui, c'est tout.

Et ce n'est pas juste une question de taille du code. Notez tout ce que vous n'avez pas à savoir à l'avance pour que ça marche. Le code est plus lisible. La signature de la fonction mieux documentée. Et il marchera mieux sur une console Windows car le support a été amélioré en Python 3.

Et encore j'ai été sympa, je n'ai pas fait la gestion des erreurs de lecture des fichiers, sinon on en avait encore pour 3 ans.

Il y a des tas d'autres trucs.

L'unpacking généralisé:

>>> a, *rest, c = range(10)  # récupérer la première et dernière valeur
>>> foo(*bar1, *bar2)  # tout passer en arg
>>> {**dico1, **dico2} # fusionner deux dico :)

Ou la POO simplifiée:

class FooFooFoo(object):
    ...

class BarBarBar(FooFooFoo):
    def wololo(self):
        return super(BarBarBar, self).wololo()

Devient:

class FooFooFoo:
    ...

class BarBarBar(FooFooFoo):
    def wololo(self):
        return super().wololo()

Python 3 est tout simplement plus simple, et plus expressif.

Bonus

Evidement il y a plein de trucs qui n'intéresseront qu'une certaine catégorie de devs:

  • ipaddress.ip_address pour parser les adresses IP.
  • asyncio pour faire de l'IO non blocante sans threads.
  • enum des enum de toutes sortes.
  • functools.lru_cache pour cacher le résultat de ses fonctions.
  • type hints pour vérifier la validité de son code.
  • l'opérateur @ pour multiplier des matrices avec numpy.
  • concurrent.futures pour faire des pools non blocantes propres.
  • statistics stats performantes et correctes.
  • tracemalloc trouver les fuites de mémoire.
  • faulthandler gérer les crash du code C proprement.

Si vous n'êtes pas concernés, ça n'est pas motivant. Mais si ça vous touche personnellement, c'est super cool.

Au passage, depuis la 3.6, Python 3 est enfin plus rapide que Python 2 pour la plupart des opérations :)

Pour finir...

Toutes ces choses là s'accumulent. Un code plus court, plus lisible, plus facile à débugger, plus juste, plus performant. Par un tas de petits détails.

Alors oui, la migration ne va pas restaurer instantanément votre érection et vous faire perdre du poids. Mais sur le long terme, tout ça compte énormément.

]]>
http://sametmax.com/tout-ce-qui-fait-que-python-3-est-meilleur-que-python-2/feed/ 31 22144
Today is a glorious day http://sametmax.com/today-is-a-glorious-day/ http://sametmax.com/today-is-a-glorious-day/#comments Sun, 12 Jul 2015 21:29:59 +0000 http://sametmax.com/?p=16600 >>> import crossbar >>> crossbar.__version__ '0.10.4' >>> import twisted >>> twisted.__version__ '15.2.1' >>> import sys >>> print('Wait for it...') Wait for it... >>> sys.version '3.4.0 (default, Apr 11 2014, 13:05:11) \n[GCC 4.8.2]' ]]> http://sametmax.com/today-is-a-glorious-day/feed/ 15 16600 Gérer plusieurs versions de Python avec “py” sous Windows http://sametmax.com/gerer-plusieurs-versions-de-python-avec-py-sous-windows/ http://sametmax.com/gerer-plusieurs-versions-de-python-avec-py-sous-windows/#comments Sat, 10 Jan 2015 09:10:27 +0000 http://sametmax.com/?p=15699 py sous Windows, qui permet de choisir quelle version de Python on lance.]]> Il est courant de vouloir installer plusieurs versions de Python. Pour faire des tests, pour s’assurer que son code est portable, pour utiliser des libs qui marchent uniquement sur une des versions, etc.

Sous Linux, c’est facile : chaque interpretteur est préfixé. Par exemple, sous Ubuntu, si je veux utiliser Python 3.4, je l’installe :

sudo apt-get install python3.4

Et si je lance python, ça me lance la 2.7 car c’est celle de base. Mais si je lance python3.4, ça me lance bien la 3.4.

Au final, on finit par utiliser des environnements virtuels qui isolent des versions de Python particulières. Mais ça ne retire pas l’envie de pouvoir choisir sa version de Python au niveau du système, ce qui reste difficile à faire sous Windows.

Or, depuis la version 3.3, l’installeur pour cet OS de Python installe la commande py, qui permet de choisir quelle version de Python on lance.

Dans un terminal

Si dans une console vous faites :

py

Il lancera le shell de votre installation Python 2.x la plus récente.

Si vous faites :

py script.py

Il exécutera le script avec votre installation Python 2.x la plus récente.

Mais vous pouvez passez un flag -version pour forcer une version de Python. Lancer le shell Python avec la 3.3 :

py -3.3

Lancer un script avec la 3.4 :

py -3.4 script.py

Cela suppose que vous avez la 3.3 et la 3.4, installés, évidement.

La syntaxe est surprenante. J’aurais pensé qu’ils mettraient un truc du genre py -i 3.4 mais non, c’est direct -numero.

La commande py accepte aussi les paramètres qu’on passerait normalement à la commande python, et notament l’option -m module, qui permet de lancer un module en particulier.

C’est pratique pour lancer ipython ou pip avec une version particulière. Par exemple pour installer autobahn uniquement pour la version 3.4 et donc utiliser asyncio :

py -3.4 -m pip install autobahn

Bang !

La commande py reconnait également la ligne shebang, cette syntaxe unix qui dit quel interpretteur utiliser. Si vous mettez sur la première ligne de votre script :

#! python3.4

Alors :

py script.py

Invoquera python 3.4.

La commande est capable de se débrouiller avec les chemins Unix, afin de garder la portabilité. Donc si vous faites :

#! /usr/bin/env python2.7

Alors :

py script.py

Va ignorer le debut de la ligne, et prendre l’installation locale de Python 2.7 pour lancer le script.

On click

Si vous installez la version Python 3.3 ou 3.4 en premier, les fichiers .py seront associés à la commande py. Donc si vous cliquez sur un script Python avec une ligne shebang, la bonne version sera lancée.

Mais si vous avez installé Python 2.x avant, il est possible que vos fichiers .py soient encore associés directement à la commande python ordinnaire.

Pour changer cela, faites un clic droit sur un fichier .py, modifiez le programme qui ouvre ce fichier et faites le pointer sur "C:\Windows\py.exe".

]]>
http://sametmax.com/gerer-plusieurs-versions-de-python-avec-py-sous-windows/feed/ 7 15699
Ca y est, on peut coder en Python 3 http://sametmax.com/ca-y-est-on-peut-coder-en-python-3/ http://sametmax.com/ca-y-est-on-peut-coder-en-python-3/#comments Mon, 17 Mar 2014 11:29:20 +0000 http://sametmax.com/?p=9790 Voici tout ce dont vous pouvez profiter avec Python 3 que vous ne pouvez pas faire avec Python 2.]]> Python 3.4 vient de sortir, et avec cette release, je peux enfin recommander aux gens de se mettre à Python 3 plutôt que Python 2. Tout simplement parce que maintenant, en plus d’avoir énormément de libs qui ont migré, on a l’expérience de la bestiole. C’est stable, les perfs sont bonnes, la conversion de code d’une version à l’autre est bien documentée et maitrisée.

Et surtout, la 3.3 et la 3.4 viennent avec tout un tas des goodies super green.

Voici tout ce dont vous pouvez profiter avec Python 3 que vous ne pouvez pas faire avec Python 2.

Des classes classes

Les classes sont des new type classes en Python 3. Pas besoin d’hériter d’object:

# Au revoir !
class TouteMoche(object):
    pass

# Bonjour !
class PropreEtNette:
    pass

Les metaclasses ne se définissent plus comme un attribut spécial :

# Au revoir !
class TouteMoche(object):
    __metaclass__ = UnTruc

# Bonjour !
class PropreEtNette(metaclass=Untruc):
    pass

Super Super()

Franchement, qui se souvient de la syntaxe pour appeler proprement la méthode parente d’une classe ?

# Au revoir !
class FranchementHein(object):

    def __init__(self):
        super(FranchementHein, self).__init__()

# Bonjour !
class FranchementHein:

    def __init__(self):
        super().__init__()

Meta meta programmation

Le module inspect permet maintenant de récupérer des infos très très précises sur la signature des fonctions :

    >>> from inspect import signature
    >>> def foo(a, *, b:int, **kwargs):
    ...     pass

    >>> sig = signature(foo)

    >>> str(sig)
    '(a, *, b:int, **kwargs)'

    >>> str(sig.parameters['b'])
    'b:int'

    >>> sig.parameters['b'].annotation
    

Et comme vous pouvez le voir, les annotations sont de la partie.

Les classes ont également un nouvel attribut __qualname__, qui, comme on peut s’y attendre, est le qualified name de la classe :

>>> class C:
...   def f(): pass
...   class D:
...     def g(): pass
...
>>> C.__qualname__
'C'
>>> C.f.__qualname__
'C.f'
>>> C.D.__qualname__
'C.D'
>>> C.D.g.__qualname__
'C.D.g'

Ca marche aussi pour les fonctions.

Enfin pour les gars qui sont vraiment tordus, on a une nouvelle méthode pour les metaclasses, __locallookup__, qui permet d’influencer le MRO à la volée.

Un yield qui yield

Bon, mon titre ne veut rien dire, c’était pour la continuité.

yield a maintenant un petit frère, yield from, qui permet de déléguer l’itération à un itérable sous jacent :

def generateur():
    yield from "123"
    yield from (str(x) for x in range(4, 7))
    yield from ("7", "8", "9")

for i in generateur():
    print(i)

## 1
## 2
## 3
## 4
## 5
## 6
## 7
## 8
## 9

Là ce n’est pas très utile, mais sur des générateurs complexes, c’est sympa. Surtout que yield from passe aussi les valeur avec send(), fait remonter les exceptions proprement et est détecté par les events loops du module asyncio, dont on parlera plus tard.

Batteries included, with charger

Pip et virtualenv sont livrés avec la 3.4 ! Rien de plus à faire, rien à installer ! Enfin !

Malheureusement pas virtualenv wrapper, mais c’est déjà pas mal.

Un nouveau format, le wheel, permet de faire des paquets binaires qui n’auront pas besoin d’être compilés sur la machine cible. Ca veut dire bientôt du numpy / scipy sans avoir à compiler quoi que ce soit. C’est un remplacement du egg plus simple et sans les problèmes de compatibilités. Il ne vise pas la distribution de projets Python standalones, comme le egg qui a notamment eu le problème de vouloir tout faire en même temps.

Pour ce dernier, le pyz est en discussion.

Enfin les scripts Python supportent maintenant l’équivalent du shebang, mais sous Windows. Ceci vient avec l’introduction de la commande py qui permet de lancer toutes les commandes Python (pip, venv, etc.) pour une version de Python installée spécifique.

Ah, non, pas “enfin”, j’ai oublié un truc :

python -X faulthandler votre_script.py vous permet maintenant d’obtenir une stacktrace sur les scripts utilisant ctypes \o/. Et le module tracemalloc permet d’enquêter sur l’allocation mémoire.

Tout un tas de trucs que les codeurs Java vont adorer

Il y a des Enums, et ça vient sous toutes les formes :

from enum import Enum, IntEnum

# La version de feignasse :
Animal = Enum('ant bee cat dog')

# La version 'anal retentive'
class Animal(IntEnum):
     ant = 1
     bee = 2

# et tout ce qu'il faut entre les deux pour les centristes

Pour les nostalgiques de l’overloading, on peut maintenant spécifier que le code d’une fonction est différent selon le type des arguments qui lui sont passés :

from functools import singledispatch

@singledispatch
def fun(arg):
    print('Comportement par défaut, teddy')

@fun.register(int)
def _(arg):
    print("Tu m'as passé un int, jonhy !")

class Cacatoes:
    pass

@fun.register(Cacatoes)
def _(arg):
    print("Tu m'as passé un Cacatoes, billy !")


fun('Do')
fun([])
fun(1)
fun(Cacatoes)

## Comportement par défaut, teddy
## Comportement par défaut, teddy
## Tu m'as passé un int, jonhy !
## Tu m'as passé un Cacatoes, billy !

Et puis le module abc pour faire des classes abstraites, même si ça a été backporté en Python 2.7, donc un peu hors sujet.

Les built-in ont un peu changé

Pour le texte, c’est de l’unicode partout, avec utf8 par défaut pour les conversions. Ca veut dire plus de u devant les chaînes de caractère, plus de déclaration de l’encoding en haut des fichiers de code. Moins de decode / encode.

Mais ça veut dire aussi que open a besoin obligatoirement d’un paramètre encoding, dont la valeur par défaut est bien entendu utf8.

Attention cependant, le built-in bytes n’est pas l’exacte équivalent du type str en Python 2 puisqu’il ne possède plus certaines méthodes de manipulation de texte comme replace:

>>> "Trololo".replace('o', 'a')
'Tralala'
>>> b"Trololo".replace('o', 'a')
Traceback (most recent call last):
  File "", line 1, in 
    b"Trololo".replace('o', 'a')
TypeError: expected an object with the buffer interface

C’est un peu chiant quand on manipule des protocoles binaires, et les mecs de mercurial ont un peu gueulé. Donc il est possible que ça change. J’ai mis beaucoup de fois le mot “peu” dans cet article, ce qui est stylistiquement très laid. Mais je suis trop paresseux pour éditer cet article qui fait maintenant 3 km.

Pour les nombres, on n’a plus à se soucier du type long, qui n’existe plus. / est maintenant la division ordinaire et // la division entière.

Pour les fonctions built-in, pas mal de changements avec bien entendu, print() qui devient une fonction, mais aussi toute ce qui est zip, map, etc, qui retournent des générateurs au lieu de listes, tout comme dict.items et consorts. Ah oui, et import est maintenant absolu par défaut.

Quelques libs en plus

Marre du module os ? pathlib permet de donner un petit goût d’objet à vos manipulations de FS, comme path.py. Ca reste moins bien, mais c’est mieux que rien.

Raz-le bol de recoder la fonction moyenne, médiane, etc ? Le module statistics a été ajouté pour ça.

Enfin, le fameux module mock, qui permet de simuler tout un tas de trucs sans tout casser :

>>> from mock import patch
>>> with patch.object(os, 'listdir', return_value=['file2.txt', 'file2.text']):
    print(os.listdir('/etc/'))
...
[u'file2.txt', u'file2.text']
>>> os.listdir('/etc/')[:2]
[u'environment', u'hosts.allow']

Tout est bien rangé

Les fichiers bytecode Python sont maintenant tous groupés dans un dossier appelé __pycache__. Finis les .pyc qui trainent partout. Et en plus prefixés de l’architecture avec laquelle ils ont été générés. Toujours utile.

En prime pas mal de noms ont été normalisés : tous les modules sont maintenant en minuscule, tous les modules liés à IO sont groupés dans le module io, urllib 1 et 2 ont été mergés, et l’arborescence des exceptions pour les erreurs d’IO a maintenant beaucoup plus de sens :

# Au revoir !
from errno import ENOENT, EACCES, EPERM

try:
    with open("document.txt") as f:
        content = f.read()
except IOError as err:
    if err.errno == ENOENT:
        print("document.txt file is missing")
    elif err.errno in (EACCES, EPERM):
        print("You are not allowed to read document.txt")
    else:
        raise

# Bonjour !

try:
    with open("document.txt") as f:
        content = f.read()
except FileNotFoundError:
    print("document.txt file is missing")
except PermissionError:
    print("You are not allowed to read document.txt")

Asyncio, la prog asynchrone rebootée

Une des raisons pour laquelle je tape sur javascript aussi fort, c’est aussi la jalousie. Ils ont tous les derniers joujoux asynchrones hi-tech, meh !

Avec Python on était obligé d’installer tornado ou twisted pour ça, et c’était un peu dommage :)

Avec la 3.4, la prog asynchrone fait peau neauve (adieu l’horrible module asyncore) et propose de la prog asynchrone plus simple, plus légère, plus facilement intégrable : asyncio.

Si vous lisez ce PEP, ça ne va pas vous parler, alors je vais potasser tout ça et faire un petit article.

Mon seul regret avec asyncio, c’est que c’est un module uniquement bas niveau, alors que tulip, le prototype de la lib, contenait des mobiles haut niveau comme par exemple un client http bien foutu. Du coup ça a donné ce genre de discussion sur le blog, avec des gens qui ne comprenaient pas que je m’attendais à un peu de haut niveau et eux qui ne voyaient que le bas, et donc s’attardaient sur de la sémentique d’implémentation de la prog non blocante.

En conclusion

Le code Python 3 est plus court, plus cohérent, avec moins de surprises, moins de dépendances externes et plus de flexibilité. Des comportements par défaut sains un peu partout, des outils en plus, et comme d’habitude vous n’en utiliserez pas la moitié tellement il y a à faire.

Python 3.4 est vraiment un très bon cru, honnêtement, avec toutes ces améliorations, le langage parvient à se maintenir au niveau des petits jeunes et de leur hype, sans sacrifier sa solidité et sa cohérence.

C’est pour ça qu’on a attendu 5 ans. C’est pour ça que la migration a été lente et prudente.

Parce que la communauté Python fait vraiment les choses bien.

Les exemples du blogs seront donc à partir de maintenant en Python 3.

]]>
http://sametmax.com/ca-y-est-on-peut-coder-en-python-3/feed/ 38 9790
Changement dans l’unpacking des iterables en Python 3 http://sametmax.com/changement-dans-lunpacking-des-iterables-en-python-3/ http://sametmax.com/changement-dans-lunpacking-des-iterables-en-python-3/#comments Fri, 20 Dec 2013 07:49:59 +0000 http://sametmax.com/?p=7656 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 !]]> 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.

]]>
http://sametmax.com/changement-dans-lunpacking-des-iterables-en-python-3/feed/ 6 7656
Conséquences de print devenant une fonction en Python 3 http://sametmax.com/consequence-de-print-devenant-une-fonction-en-python-3/ http://sametmax.com/consequence-de-print-devenant-une-fonction-en-python-3/#comments Tue, 03 Dec 2013 08:31:37 +0000 http://sametmax.com/?p=8164 print() au lieu de print m'arrache la gueule, je dois l'avouer. J'ai l'impression que ces deux parenthèses ma prennent 5 heures à taper, là où avant mon petit espace était à portée de pouce. Mais évidement, la décision de faire de print une fonction est parfaitement rationnelle, et en voici tous les avantages...]]> Devoir utiliser print() au lieu de print m’arrache la gueule, je dois l’avouer. J’ai l’impression que ces deux parenthèses ma prennent 5 heures à taper, là où avant mon petit espace était à portée de pouce.

Mais évidement, la décision de faire de print une fonction est parfaitement rationnelle, et en voici tous les avantages…

print n’est plus une déclaration, mais une expression

Il y a deux types d’instructions en Python : les déclarations (‘statement’ dans la langue de Chuck Norris) et les expressions.

Les déclarations sont des instructions indépendantes : while, var = 'valeur', try, def, etc. On ne peut pas les mettre dans une expression, seulement les imbriquer dans une autre déclaration.

Les expressions, elles, sont imbricables dans n’importe quoi, et elles retournent toujours quelques chose quelque chose, fusse None. Parmis les expressions on retrouve : les lambdas, les listes en intentions, les appels de fonctions, etc.

Avant, print était une déclaration, ce qui était très peu souple. Maintenant, c’est un appel de fonction, et donc une expression.

On peut donc utiliser print dans une autre expression et ainsi :

Dans une lambda :

crier = lambda x, *a, **k: print(x.upper(), *a, **k)

Dans une liste en intention :

[print(x) for x in range(10)]

Plus de syntaxe bizarre

Comment supprimer le saut de ligne quand on print avec l’ancienne syntaxe ?

print "Hello", # <- notez la virgule
print "You"
Hello You

Avec la nouvelle syntaxe :

print("Hello ", end='')
print('You')

Comment rediriger le print vers stderr avec l'ancienne syntaxe ?

>>> print >> sys.stderr, 'arg !'

Avec la nouvelle ?

>>> print('arg !', file=sys.stderr)

Comment faire une liste séparée par des virgules avec l'ancienne syntaxe ?

>>> l = ['Des petits trous', 'des petits trous', 'toujours des petits trous']
>>> print ', '.join(l)
Des petits trous, des petits trous, toujours des petits trous

Avec la nouvelle ?

>>> print(*l, sep=', ')

En gros, la syntaxe est unifiée, plus besoin de penser à tous ces cas particuliers. Et en plus on peut demander de l'aide avec help(print) alors qu'avant ça faisait une syntaxe error.

On peut récupérer une référence sur

print

Et donc la passer en paramètre pour faire de l'injection de dépendance :

def truc(a, print=print):
    # faire un truc avec a
    print(a); # on peut utiliser print normalement

import logging
log = loging.getLogger()
truc(machin, print=log) # on print pas, on log !
truc(autre_machin, print=lambda *a, **k: None) # ignore les prints !

Et également appliquer tous les outils de programmation fonctionnelle de Python :

import sys
from functools import partial
error = partial(print, file=sys.stderr)
error('Wrong !') # va directement sur stderr

Activer print() en Python 2.7

Si vous voulez prendre toute de suite de bonnes habitudes, vous pouvez faire, en Python 2.7 :

from __future__ import print_function
]]>
http://sametmax.com/consequence-de-print-devenant-une-fonction-en-python-3/feed/ 13 8164
Les annotations en Python 3 http://sametmax.com/les-annotations-en-python-3/ http://sametmax.com/les-annotations-en-python-3/#comments Wed, 06 Nov 2013 06:05:43 +0000 http://sametmax.com/?p=7602 Mais oui, on va parler un peu plus de Python 3 à partir de maintenant \o/

C’est qu’avec la prochaine version 3.4 qui apporte tellement de bonnes choses, Django 1.6 pleinement compatible et des libs comme Pillow et six pour combler les trous, ça y est, on peut enfin s’y mettre.

Commençons donc par les annotations, feature simple, et qu’on ne retrouve pas backportée en 2.7.

D’abord, à quoi servent les annotations ?

A rien !

Non, non, je ne déconne pas. Non seulement les annotations sont parfaitement optionnelles, mais en plus elles n’ont aucun effet.

Je répète pour que ça rentre : vous n’avez pas à utiliser les annotations. Aucun programme Python n’a besoin d’utiliser les annotations. Vous pouvez programmer votre vie entière en Python sans avoir jamais à utiliser les annotations. Et si vous les utilisez, elles ne changeront rien à votre programme.

Point.

Non, mais sérieux, ça sert à quoi ?

Python est un langage très souple, et une raison de sa souplesse c’est que la nature des données que vous traitez est dynamiquement découverte à l’exécution. Particulièrement, le type des variables est dynamique. La portée des variable est dynamique.

Avantages :

  • Le code est court et clair.
  • Le langage est très facile à apprendre.
  • On peut utiliser le duck typing.

Inconvénients :

  • Il faut écrire des informations en plus dans la doc.
  • Le code est plus lent car la VM ne peut optimiser le code pour les types.
  • Les IDE ont moins d’outils pour aider à la rédaction du code.

C’est la nature de Python. Pour certains cas, c’est ce qu’on veut. Dans d’autres cas où ce n’est pas adapté, il faut choisir un autre langage.

Les annotations sont là pour corriger ce point : on va permettre de fournir des informations en plus, si, et seulement, si, on le souhaite.

Ces informations n’auront aucun impact sur l’exécution du code. La VM Python va les ignorer. Mais un outil extérieur pourra potentiellement les utiliser pour en faire quelque chose.

Show me the code !

Faites chauffer votre interpréteur de Python 3…

On peut annoter uniquement les paramètres d’une fonction (ou méthode) :

>>> def ma_fonction(param1: "Une annotation", param2: 1 + 1, param3: int):
...     print(param1, param2, param3)
...
>>> ma_fonction(1,2,3)  # une annotation ne change absolument RIEN
1 2 3

L’annotation se note donc ainsi : nom du paramètre: annotation.

L’annotation PEUT être n’importe quelle expression Python valide. En fait l’annotation DOIT être une expression valide. Et cette expression sera exécutée une, et une seule fois, à l’initialisation du module. Le résultat sera stocké et accessible, via l’attribut __annotations__ :

>>> ma_fonction.__annotations__
{'param1': 'Une annotation', 'param2': 2, 'param3': }

Évidément, les annotations se mélangent sans problème avec les cas avancés de paramétrage :

>>> def ma_fonction(param1 : "Une annotation" = "valeur par défaut", *param2: "Autre annotation"):
... print(param1, param2)
...
>>> ma_fonction()
valeur par défaut ()

On peut également spécifier une annotation pour la valeur de retour :

>>> def ma_fonction() -> None: # un petit goût de coffeescript
...     pass
...
>>> ma_fonction.__annotations__
{'return': None}

Et combiner tout ce bordel pour faire des trucs bien compliqués qui vous garantissent la sécurité d’emploi, ce qui manque cruellement en Python (les trucs compliqués, pas la sécurité d’emploi…):

>>> def ma_fonction(param1: lambda x: x * 2,
...                 param2: (lambda x: x * 2)(2) = 5,
...                 **params : "Keywords parameters rocks") -> list:
...     res = [param1, param2]
...     res.extend(params)
...     return res
...
>>> ma_fonction(1, 2, param3=3, param4=4)
[1, 2, 'param4', 'param3']
>>> ma_fonction.__annotations__
{'return': , 'param1':  at 0x7f77a2982e60>, 'param2': 4, 'params': 'Keywords parameters rocks'}

Les lambdas ne peuvent pas être annotées par contre.

Le potentiel des annotations

Les annotations sont le cas typique d’une fonctionnalité bac à sable. En clair, sur la mailling list il y a eu de nombreuses requêtes pour permettre de la vérification de type, de l’auto documentation, de la translittération et autres joyeusetés.

Ne sachant pas comment répondre de manière propre à ces demandes, ni lesquelles étaient les plus prioritaires, les annotations ont été créées. Le but et de laisser maintenant la communauté fabriquer les outils, et ainsi :

  • Ceux pour les fonctionnalités les plus utilisées seront naturellement priorisées.
  • Les standards pour les annotations vont émerger par l’usage, et pas parce que Guido l’a décidé depuis son bureau au doigt mouillé.
  • Les fonctionnalités du genre “je veux un poney” seront tout simplement ignorées puisque personne ne travaillera suffisamment dessus (quand quelque chose est un caprice, le capricieux se sort rarement les extrémités manuelles de la cavité rectale.)

Des outils comme pyanno donnaient déjà un exemple de ce que la communauté pouvait désirer. Reste à savoir si ce sera vraiment utilisé. Les gens qui font du Python sont rarement fan du Java. Mais le caractère optionnel de la chose pourrait bien ajouter un vrai plus au langage sans l’alourdir.

Voici donc ce qu’on peut attendre des outils tierces parties qui utiliseront les annotations dans le futur :

Amélioration des performances

L’utilisation de RPython pour coder Pypy a montré que les perfs de Python pouvaient être fantastiques, si on rajoutait quelques annotations. RPython, est, je le rappelle, un subset de Python (donc tout code RPython tourne dans l’interpréteur Python normal), mais avec des annotations de type, sous forme de commentaires.

On peut donc imaginer que dans le futur, les codes en fort besoin de perfs comme pour l’embarqué ou le traitement scientifique, pourront ajouter dans les parties clés de l’algo des annotations pour booster la vitesse ou diminuer la mémoire consommée. On parlerait ici alors d’annotation de type. int, list, UneClasse sont en effet des expressions Python valides…

L’avantage de l’annotation, c’est son caractère purement optionnel, qu’on peut ajouter seulement à certains codes, ou à une partie du code.

Aide à la saisie

Les IDE ont du mal avec Python. La complétion du code, l’aide en popup, la refactorisation automatique et toutes ces joyeusetés sont rarement bien implémentées.

Dans ma vie de tous les jours, je m’en branle. Je code tellement vite en Python, je ne m’en aperçois même pas. Mais quand j’utilise une nouvelle lib, ça peut être utile.

Typiquement, pour un code comme celui de 0bin, je ne me servirais pas des annotations. Mais pour une lib comme minibelt, je prendrais peut être le temps de mettre des annotations de type, pour aider ceux qui vont l’utiliser. Ou une annotation pour signaler les exceptions que certaines fonctions peuvent lever.

Génération de documentation

Certains voient les annotations comme un complément aux docstrings. Ce n’est pas mon cas, mais je vous le note, au cas où ça devienne une tendance.

Vérification de Type

Dans la même lignée de l’aide à la saisie, pour certaines fonctions (c’est rare, mais ça arrive), on veut éviter le duck typing et forcer l’utilisateur à passer un type en particulier. Avoir cette possibilité via les annotations pourraient être utile.

Je suis septique sur cet usage, en effet des libs existent depuis des années pour faire cela, et plus, via des décorateurs, et ça n’est pas très utilisé. La différence est qu’un mécanisme standard pourra être détecté par les IDE.

Après on risque d’avoir des abus pour ce genre de fonctionnalités. Je vois d’ici arriver les codeurs d’autres langages caféinés, essayant de pousser l’usage d’une annotation rendant des attributs private :-)

Heureusement, les annotations seront normalement pour toujours optionnelles.

Translittération de code

Vous avez un code Python et vous voulez le transformer en code Cobol ? Bon, le compilateur de Pypy a déjà le potentiel de le faire, mais avec les annotations, il est potentiellement possible de rajouter suffisamment d’informations pour qu’on puisse toujours transformer un code de Python vers un autre langage Turing complet.

J’ai dit potentiellement.

Je répète une dernière fois…

Les annotations ne font rien. Elles n’ont pas le but de faire quelque chose et de toute façon sont optionnelles.

Leur but est de permettre à la communauté de créer des nouveaux outils utilisant les annotations et voir lesquels seront les plus utilisés, matures et désirés.

Pour le moment, il n’y a pas grand chose de fait avec, mais jusqu’ici Python 3 n’a pas été très populaire. Cela risque de changer avec 2014 qui propulsera Python 3.4 dans la cour des grands.

]]>
http://sametmax.com/les-annotations-en-python-3/feed/ 15 7602
Mon premier projet Python3 http://sametmax.com/mon-premier-projet-python3/ http://sametmax.com/mon-premier-projet-python3/#comments Thu, 03 Oct 2013 18:37:01 +0000 http://sametmax.com/?p=7281

Ceci est un post invité de Coyote posté sous licence creative common 3.0 unported.

Et oui, il fallait bien que ça arrive.

La genèse

Bon, ça avait commencé doucement. Il y a de ça quelques temps (avant même l’article de Sam), j’avais configuré mon Sublimissime pour qu’il me force les import du __futur__.
Étant moi-même un peu un nazi de la convention de code, ça ne m’a pas dérangé plus que cela: les keyword print et autres sont pas trop ma tasse de thé. Par contre, c’est vrai que ça m’a un peu forcé la main sur les import relatifs, que j’utilisais visiblement un peu trop.

Récemment, sur un autre projet, je me dis “allez, je passe à la syntaxe "hello {}".format("world") au lieu des "hello" % "world"” puisque c’est ce qui est désormais recommandé. Bon, c’est un peu chiant au début mais on s’y fait vite.

Aussi, j’ai adopté la suppression des prefix u devant les strings. Je sais qu’avec Python 3.3 on peut à nouveau les utiliser, mais vraiment, j’ai toujours trouvé ça moche et insupportable, alors je suis très content de ne plus avoir à me les farcir.
Que ceux que je vois râler me disent comment ils justifient qu’avec ce temple de la simplicité et de la lisibilité qu’est Python, on doivent préfixer toutes nos chaines par des petits u ridicules.

Voilà pour l’historique, lentement mais surement, je me préparais à prendre peut-être de bonnes habitudes pour le jour (très) lointain ou Python3 deviendrait réalité.

Et c’est là que mon collègue, un pervers à lunettes, nous propose d’utiliser PY3 pour de vrai, sur un nouveau projet.

C’est vrai que le projet s’y prêtait bien: un petit site web de CRUD avec Django. On s’est gratté le menton deux minutes et puis on s’est dit “pourquoi pas ?”.

Par où commencer ?

C’est con hein, mais la première chose que tu fais, c’est te demander qu’est ce qui change entre PY2 et PY3.
Et bien la réponse est pas simple du tout et si tu crois que tu vas trouver une URL avec une liste de chose à changer, tu te trompes lourdement.

Le problème est que PY3.0 avait sans doute dû être fini à la pisse et que donc les choses ont beaucoup évoluées entre 3.0, 3.1, 3.2 et 3.3. J’ai lu beaucoup de posts sans importance sur toutes ces étapes intermédiaires, mais la conclusion, c’est que grosso-modo, il faut passer de 2.7 à 3.3. Entre les deux, c’est un peu comme naitre au Sahara Occidental.

Installer PY3.3

Comme on travaille en équipe, on a des setup différents: OSX, Ubuntu 12.04 et Ubuntu 13.04.

Ça c’est passé sans douleur malgré les appréhensions. Pour Ubuntu, il suffit d’utiliser le ppa deadsnakes.
Une fois installé, un coup de virtualenv (mkvirtualenv -p /usr/bin/python3.3 monenv3) et c’est parti.

Les dépendances

Comme expliqué plus haut, on a choisi de faire ce projet en PY3 parce qu’il est petit et sans dépendances ; donc pas très représentatif.
Cependant, on utilise:
Django==1.5.4
South==0.8.2
django-mptt==0.6.0
django-picklefield==0.3.0
numpy==1.7.1
unicodecsv*

Aucun problème avec ceux là. On a cru que South nous faisait des misères mais en fait c’était un import relatif avec un pass de cochon qui foutait la merde (try: from local_settings import * except ImportError: pass.
Donc la leçon ici, c’est que les imports relatifs c’est mal.

Au niveau des dépendances, on a du se séparer de batbelt. Oui, je parle bien de sametmax/Bat-Belt qui n’est pas du tout PY3 proof. Ne pleurez pas, moi aussi j’ai été très déçu et j’attends des explications (foireuses, je les vois venir d’ici).

Bref, comme on utilise batbelt pour presque rien, on a copié honteusement (ou pas) le code nécessaire.

Dernière remarque, vous voyez dans la liste unicodecsv. C’est un peu tricky puisque PY3 supporte unicode par défaut, le module csv de PY3 fonctionne directement ; et de fait, unicodecsv n’existe plus. Oui, je me suis fait avoir comme un débutant.
Bref, comme on souhaite aussi supporter PY2 et pas uniquement PY3, on a un beau if PY2: import unicodecsv as csv else: import csv.

Les problèmes

Peu nombreux je dois dire et c’est plutôt encourageant. De tête:

  • Fini les __unicode__() dans les modèles Django.
    Pour gérer ce cas à la con, on utilise ce fichier _compat de Jinja2. Je vous encourage à le copier, il défini notamment la variable PY2 pratique pour switcher comme le cas du csv ci-dessus.
    Bref, dans ce _compat, il y a un class decorator implements_to_string qui permet de ne définir que __str__() (renvoie de l’unicode) et le reste est géré automatiquement.
    Django propose la même chose via django.utils.encoding.python_2_unicode_compatible mais je préfère la toolbox Jinja.
  • J’ai parlé dans les dépendances du cas unicodecsv. Ici c’était le seul, mais clairement dans beaucoup de projet qui manipulent des stream ou des fichiers, il va falloir faire ce genre de combines pour suporter PY2 et PY3.
  • Un edge case très rigolo (après coup!) avec le django-picklefield. Le field se comportait correctement avec PY3 mais pas avec PY2 et pas dans tous les cas. Du bonheur à tarinerdéboguer.
    Pour l’anecdote:
    x = MyModel(id=1)
    x.picklefield = [1,2,3]
    x.save()

    x = MyModel(id=2)
    x.picklefield = {'a': 1, 'b': 3}
    x.save()

    Ce code fonctionne bien avec PY3, mais avec PY2, le fait de réutiliser le même nom de variable rend le PickleField tout chose et donc il sérialise deux fois la valeur passée. Encore une fois, la leçon ici est d’être rigoureux.

  • Les fonctions du type dict.keys() et assimilés ne renvoient plus de listes, mais ont été remplacés par leur équivalents générateurs (dict.iterkeys()) et ceux-ci n’existent plus. Donc un peu de gymnastique syntaxique quand on est habitué à l’ancienne syntaxe.
  • Numpy refusait de s’installer sur certaines versions d’Ubuntu (je me souviens plus laquelle) car il ne trouvait pas un des headers de Python. C’est du au fait qu’il cherche ce header dans le même répertoire que le header précédent alors qu’il est stocké ailleurs. Un symlink fait l’affaire.
  • Enfin, le dernier problème, qui va réveler que tout ceci n’est qu’une imposture: chaussette et circus que j’utilise pour le déploiement ne sont pas compatibles PY3 !! Pas de debug ici, ça ne s’installe même pas. Résulalt: on a déployé en PY 2.7. Bon, pour vous consoler (et moi avec), le github de ces 2 projets est plein de commits récents en rapport avec PY3 donc j’imagine que ça ne saurait tarder.

Conclusion

Et bien, on a un petit projet développé à plusieurs, en PY3.3, qui est déployé en PY2.7 donc c’est possible ; on a pas rencontré de grande difficultées donc je pense que pour du web, c’est pas loin d’être mûr.

Pas grand chose à dire finalement, et c’est sans doute ça le plus important !

Allez bookmarker le site python3porting qui contient les infos sur ce qui a changé. Ca a l’air long mais en fait, y’a beaucoup de cas spécifiques.

]]>
http://sametmax.com/mon-premier-projet-python3/feed/ 7 7281
Dois-je apprendre Python 2 ou Python 3 ? http://sametmax.com/dois-je-apprendre-python-2-ou-python-3/ http://sametmax.com/dois-je-apprendre-python-2-ou-python-3/#comments Wed, 12 Sep 2012 13:57:39 +0000 http://sametmax.com/?p=2098 J’ai écris cet artice en 2012. La réponse a depuis changé :

Apprenez Python 3.

Il est plus simple, plus élégant, plus mieux.

Installez minimum Python 3.4, si possible.

Quelques exceptions à cette règle :

  • Vous planifier d’utiliser une lib en rouge dans cette liste.
  • Vous savez que vous allez bosser essentiellement sur du vieux code dans votre boîte.

Rappelez-vous qu’il est facile d’installer et utiliser Python 2 et 3 sur la même machine. Et un virtualenv peut contenir une version de Python particulière. Le choix de l’un n’exclus donc pas l’autre pour plus tard. On peut même écrire un code qui marche sur Python 2 ET Python 3 aujourd’hui. C’est moche, mais ça marche :)

Donc, bonjour Python 3 avec pip et venv installés par défaut, les accents qui marchent out of the box, pas de bizarreries qui traînent comme des noms incohérents et object

Mac et Linux ont la 2.7 par défaut installé, mais ça se règle avec un coup de homebrew, apt-get install, yum install, etc. N’ayez pas peur.

Mon ancien article n’est donc plus valable.

Les deux versions étant incompatibles, et Python 3 ayant plein de super fonctionnalités que Python 2 n’a pas, mais la V2 étant encore très répandue, on peut se demander lequel on doit apprendre.

Etat du lard

Python 3 permet de s’affranchir de beaucoup de problèmes d’encoding en utilisant unicode par défaut. A partir de la version 3.3, il va venir avec un virtualenv intégré, et distutils2, rendant le packaging et le déploiement beaucoup plus simple. Sans compter la délégation à un sous générateur. Plus besoin non plus d’utiliser ‘object’ dans l’héritage, et des tas de libs nettoyées. Bref, un comportement plus simple et un langage plus beau.

Python 2 lui est néanmoins présent encore partout:

  • Ubuntu est toujours en 2.7, et passera en 3 dans la prochaine version, mais laissant la possibilité d’installer la 2.
  • Mac OS utilise toujours la 2.6 par défaut.
  • Certains serveurs Web sont toujours en 2.4 (!) si bien que Max recompile carrément Python 2.6 dessus (oui, c’est un acharné).
  • Django est en phase de transition version Python 3, mais pour le moment ce n’est pas fait, et il sera toujours compatible avec Python 2.7 pour les deux prochaines versions.
  • Une bonne partie des libs sur pypi et sur github sont toujours en Python 2, ou au moins compatibles avec.

Enfin, l’outil 2to3 permet de convertir automatiquement, et facilement, la plupart des codes de Python 2 vers Python 3.

Apprentissage et usage

Dans la vie de tous les jours, en tant que programmeur Python, vous allez donc forcément être confronté à Python 2 pendant encore 3 ou 4 ans. Je rappelle que Python 2.4 était encore assez courant il y a deux ans, et qu’il y a 5 ans je lisais encore des questions sur les fora concernant Python 2.2.

Or, Python 2 est plus compliqué que Python 3, il demande des connaissances particulières: gérer l’encoding, setuper son virtualenv, faire gaffe à bien utiliser ‘object’, connaitre les noms des libs mal standardisés, etc.

Dans tous les cas, il sera toujours beaucoup plus simple d’apprendre Python 2 et de passer à Python 3 que l’inverse, et il est improbable que vous n’ayez pas besoin de toucher un code en Python 2. Hey, nous même, nous codons encore tous nos projets en Python 2. On se posera la question de la migration quand Django l’aura fait, et même là, on aura des tas de projets qui resteront pour toujours en V2.

Donc, si vous apprenez (ou enseignez le Python), le choix le plus pragmatique est d’apprendre avec Python 2, et de rajouter l’apprentissage des différences entre la V2 et la V3. Ce faisant vous saurez aussi comment faire migrer un projet, ce dont il est possible que vous ayez un jour besoin.

]]>
http://sametmax.com/dois-je-apprendre-python-2-ou-python-3/feed/ 26 2098