Sam & Max: Python, Django, Git et du cul » python 3 http://sametmax.com Deux développeurs en vadrouille qui se sortent les doigts du code Wed, 05 Feb 2014 14:20:37 +0000 en hourly 1 http://wordpress.org/?v=3.3.1 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 Sam 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.

flattr this!

]]>
http://sametmax.com/changement-dans-lunpacking-des-iterables-en-python-3/feed/ 6
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 Sam 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

flattr this!

]]>
http://sametmax.com/consequence-de-print-devenant-une-fonction-en-python-3/feed/ 13
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 Sam 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': <class 'int'>}

É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': <class 'list'>, 'param1': <function <lambda> 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.

flattr this!

]]>
http://sametmax.com/les-annotations-en-python-3/feed/ 12
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 coyote 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.

flattr this!

]]>
http://sametmax.com/mon-premier-projet-python3/feed/ 7
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 Sam http://sametmax.com/?p=2098 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.

flattr this!

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