Archive for décembre, 2016

Les fonctions anonymes lambda en Python : print, expressions conditionnelles et récursivité

samedi, décembre 24th, 2016

Si Python n’est pas un langage de programmation fonctionnelle, il possède cependant des fonctions anonymes lambda qui sont typiques de cette famille de langages. Ces fonctions sont réputées peu puissantes en Python car elle ont été volontairement limitées syntaxiquement à une expression, sans possibilité d’utiliser des instructions. Pourtant, nous allons voir qu’elles ont dans ce langage quelques particularités intéressantes.

Print

L’instruction print est devenue une fonction print() – et donc une expression – dans Python 3 suite à la PEP 3105. Ainsi, si vous utilisez print dans une fonction lambda, Python 2 lèvera une exception SyntaxError: invalid syntax alors que Python 3 l’acceptera :

>>> pr = lambda x : print(x)
>>> pr('OK en Python 3')
OK en Python 3

Expressions conditionnelles

Introduites (tardivement) dans Python 2.5 suite à la PEP 308, les expressions conditionnelles sont une manière simplifiée de réaliser grâce à l’opérateur ternaire true_value if condition else false_value la suite d’instructions suivante :

if condition:
    x = true_value
else:
    x = false_value

Comme leur nom l’indique, les expressions conditionnelles sont bien des expressions et elles permettent donc de mettre de la logique dans les fonctions lambda. Cela était déjà possible précédemment, en abusant un peu les opérateurs logiques classiques avec condition and true_value or false_value, mais c’est une méthode que je déconseille car elle n’est pas totalement fiable pour certaines valeurs de condition.

Dans l’exemple suivant, j’utilise une expression conditionnelle avec la fonction print() et donc Python 3, ce dernier me permettant d’utiliser un nom de variable avec le caractère non-ASCII é (PEP 3131) :

>>> majorité = lambda x : print("mineur") if x < 18 else print("majeur")
>>> majorité(15)
mineur
>>> majorité(25)
majeur

Récursivité

Le principe des fonctions anonymes étant de ne pas être nommées, il est donc logiquement difficile de les appeler. Ainsi, les fonctions anonymes de certains langages fonctionnels ne peuvent pas s’appeler, et donc ne peuvent pas être récursives. En Python, les lambda sont un sucre syntaxique limité des fonctions normales mais elles leur sont sémantiquement équivalentes, et elles peuvent donc parfaitement s’appeler récursivement.

En utilisant une expression conditionnelle et la récursivité on peut ainsi facilement implémenter l’algorithme récursif naïf de la très classique suite de Fibonacci :

>>> fib = lambda x : x if x < 2 else fib(x - 1) + fib(x - 2)
>>> fib(1)
1
>>> fib(10)
55
>>> fib(25)
75025

N’essayez pas d’aller beaucoup plus haut pour tester cet algorithme de complexité exponentielle, mais il démontre bien la puissance quelque peu surprenante des fonctions lambda en Python.

Compréhension de liste

On peut enfin ajouter que l’usage de compréhension de liste permet aisément de faire une boucle dans une fonction lambda :

>>> incr = lambda liste : [i + 1 for i in liste]
>>> incr([1, 45, 340])
[2, 46, 341]

Base LEGI et système de fichiers : ext4 vs XFS

jeudi, décembre 8th, 2016

Comme je l’indiquais dans mon article sur la base LEGI, cette dernière est assez volumineuse et structurée d’une manière très complexe. Ainsi, la dernière version de la base est composée de 1 624 783 fichiers XML, répartis dans une arborescence absconse de 1 537 949 sous-répertoires pour une taille d’une dizaine de Go.

Cette structure est suffisamment extrême pour nous amener à nous interroger sur le choix et sur les performance de notre système de fichiers, alors que la plupart des gens utilisent un système de fichiers sans même en avoir vraiment conscience et a fortiori sans le choisir.

Le première chose si vous souhaitez travailler sur la base LEGI, qui est composée d’un très grand nombre de petits fichiers, c’est de privilégier l’utilisation d’un SSD à celle d’un disque dur classique. En effet, les performances seront 10 à 20 fois meilleures avec un SSD.

Les systèmes de fichiers sont un sujet très technique et de très bas niveau, sur lequel peu de personnes sont compétentes et où les convictions affichées relèvent parfois plus de la croyance que de l’analyse scientifique. Voici donc trois éléments de comparaison objectifs et compréhensibles des systèmes de fichiers ext4 – le choix par défaut sous Linux – et XFS.

1) Taille de la base LEGI

Dans mon article je mentionnais que la base LEGI pouvait varier de taille selon le système de fichier, sans citer explicitement ext4 et XFS.

ext4 : 15 Go
XFS : 9 Go

Pourquoi une telle différence ? C’est Jean-Baptiste Denis qui m’a aidé à percer ce mystère. En fait XFS possède des Shortform Directories qui permettent de stocker les petits répertoires directement dans leur inode. Les 6 Go supplémentaires correspondent donc aux 1 537 949 blocs de 4 Ko créés par ext4 pour chacun des sous-répertoires.

Vainqueur : XFS

2) Nombre d’inodes

Un inode est utilisé par fichier et par répertoire lors de la décompression de la base LEGI. Il faut donc que la partition dans laquelle est stockée la base possède au minimum 1 624 783 + 1 537 949 = 3 162 732 inodes. Or le nombre d’inodes varie selon les systèmes de fichiers et les options de formatage. Pour visualiser le nombre d’inodes de vos partitions il suffit d’utiliser la commande df -ih.

ext4 : 65 000 inodes/Go
XFS : 1 000 000 inodes/Go

Ceci n’est pas du tout anecdotique, car beaucoup d’hébergeurs ne permettent pas de choisir votre système de fichier : ce sera ext4 avec ses options de formatage par défaut et rien d’autre. Avec seulement 65 000 inodes par Go, il faudra une partition d’une taille minimale de 50 Go pour pouvoir stocker la base entière. Cela implique que certaines offres de VPS peu chères, avec une capacité de stockage SSD de petite taille, ne vous permettront pas d’exploiter la base LEGI.

Vainqueur : XFS

3) Performances

J’ai évalué les performances des deux systèmes de fichiers avec plusieurs commandes parcourant la base LEGI sur un serveur Xeon 8 cœurs 3,7 GHz doté de 16 Go de RAM et d’un SSD. Les résultats permettent de comparer Ext4 et XFS, mais les performances sur votre ordinateur risquent d’être nettement inférieures.

J’ai utilisé la commande echo 3 | sudo tee /proc/sys/vm/drop_caches pour vider les caches avant chaque essai (merci Jean-Baptiste bis).

Commandeext4XFSext4/XFS
du -hsc legi3'08"0'53"3,5
find legi -type d | wc -l3'06"0'56"3,3
find . -name "*.xml" | wc -l2'54"0'51"3,4
tar xzf Freemium_legi_global.tar.gz2'26"1'18"1,9

On peut ici conclure que XFS se révèle globalement 3 fois plus rapide qu’ext4.

Vainqueur : XFS

XFS sort donc grand vainqueur de cette comparaison avec ext4, et je ne peux que vous encourager à l’utiliser si vous voulez exploiter la base LEGI. À titre personnel, j’ai décidé de ne plus utiliser que XFS.

Ruby est mourant (Python, Node.js, Go et Elixir l’ont tué)

lundi, décembre 5th, 2016

En août 2015 GitHub publiait le classement des langage de programmation les plus populaires depuis son lancement en 2008. L’article mettait l’accent sur la progression de Java, lié à l’adoption des services de GitHub par les entreprises friandes de ce langage, mais c’est bien l’évolution de la popularité de Ruby qui m’avait le plus interpellé.

Leader aux premiers jours de 2008, avec en particulier la présence immédiate de Ruby on Rails sur GitHub (GitHub est une application Ruby en Rails), Ruby est passé 2ème en 2013, puis 3ème en 2014 et enfin 4ème en 2016.

github

Suivant de près la popularité des langages sur GitHub, j’ai eu la chance de capturer hier l’improbable moment où PHP – le grand-père du web en net regain de forme depuis la sortie de PHP 7 – a dépassé l’enfant prodige pour le reléguer à la 5ème place !

classment2

Mais si Ruby est mourant, quels sont donc ses meurtriers ?

Python pour la diversité d’utilisation

Python est un peu le concurrent historique de Ruby, tant ces deux langages semblaient occuper la même « niche » de langage de programmation moderne, dynamique et orienté objet. Ce que l’on peut faire avec Python on peut le faire avec Ruby, et réciproquement, le choix entre les deux est plus une question de feeling sur la syntaxe du langage ou l’ambiance de la communauté.

Oui mais justement, que fait-on avec ces langages ? En première page du site web du langage Python on peut trouver une section « Use Python for… » qui donne une réponse à cette question dans le but de conforter les débutants dans leur choix de Python : ils ne vont pas seulement apprendre un langage de programmation puissant et facile d’utilisation, mais aussi un outil pour faire plein de choses utiles et très diverses.

python

Car si Python est bien sûr utilisé pour la programmation web et qu’il a son alter ego de Rails avec Django, il est aussi énormément utilisé dans bien d’autres domaines. Python est installé par défaut sur toutes les distributions GNU/Linux et sur macOS, il est largement utilisé en administration système, s’est imposé pour des utilisations scientifiques et pédagogiques et représente le choix le plus simple et efficace pour développer des interface graphiques, avec de nombreux bindings vraiment fonctionnels et maintenus.

Ruby et sa communauté sont en comparaison par trop web-centric. Rails, Sinatra et Jekyll sont formidables, et ils étaient même très innovants, mais de nombreux projets s’en sont inspiré, des équivalents ont été développés dans d’autres langages et ils ne peuvent plus à eux seuls justifier d’apprendre le Ruby. Ruby a percé grâce à Rails et au web, mais il n’a jamais su s’en émanciper et c’est ce qui est en train de causer sa perte.

Node.js pour le buzz

D’une manière qui a parfois pu irriter les pythonistes, Ruby a longtemps eu l’image d’un langage cool, et tout ce qui s’y rapportait de près ou de loin faisait facilement le buzz. Oui mais voilà, Node.js est passé par là, amenant au JavaScript l’ubiquité, cette exceptionnelle singularité d’être le seul langage de programmation présent à la fois côté serveur et côté client.

trend

Node.js a bénéficié d’une large et dynamique communauté préexistante de développeurs JavaScript, ce qui a permis son adoption à une vitesse incomparable à celle d’un « nouvel entrant ». Node.js a donc attaqué frontalement Ruby sur sa niche web tenue par Ruby on Rails et l’a en quelque sorte dépossédé de son buzz.

Go et Elixir pour les performances

Deux langages de programmation récents ont aussi causé beaucoup de tort à Ruby en l’attaquant sur le terrain des performances, son point faible historique. On peut ainsi développer rapidement de beaux et fonctionnels sites web avec Ruby on Rails, mais quand le succès arrive, il est alors très difficile de scaler. Le remplacement de Ruby on Rails par du Java par Twitter en 2011 en est bien sûr l’exemple le plus cinglant.

Go est un langage de programmation compilé créé par Google en 2007 et rendu disponible en 2009. Alors que l’on pensait que ce langage allait attirer les développeurs C et C++ à la recherche de plus de confort, il a en fait attiré beaucoup de développeurs de langages dynamiques à la recherche de plus de performances. Ainsi de nombreuses personnes réécrivent leur application Rails en Go et constatent des gains de performances leur permettant par exemple de passer de 30 à 2 serveurs pour l’hébergement, ce qui implique des économies de travail, de temps et d’argent colossales.

Elixir est un langage de programmation fonctionnel datant de 2012 qui utilise BEAM, la formidable machine virtuelle massivement concurrente développée pour Erlang. La particularité d’Elixir est de reprendre la sémantique d’Erlang et de la proposer aux développeurs sous la forme d’une nouvelle syntaxe… inspirée du Ruby ! Cela n’est pas très surprenant quand on sait qu’Elixir a été créé par José Valim, un des principaux développeurs de Ruby on Rails. Et ce sont bien sûr les problèmes de performance de Ruby qui ont motivé sa démarche. Elixir se positionne donc clairement comme un nouveau Ruby, mais qui scale.

Il est très probable que Go et Elixir vont continuer à devenir de plus en plus populaires dans les années qui viennent, et que Ruby va avoir beaucoup de difficultés à s’en relever. Un dernier petit exemple en passant, sur les cinq logiciels libres dont je parlais dans mon article sur la base LEGI, deux sont en Python, un en PHP, un en Go, un en Julia… et aucun en Ruby.