IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Python est facile. Go est simple. Simple != Facile
Python et Go ont des qualités distinctes qui peuvent se compléter, par Preslav Rachev, ingénieur en informatique

Le , par Preslav Rachev

114PARTAGES

18  0 
On croit souvent à tort que les termes "simple" et "facile" désignent la même chose. Après tout, si quelque chose est facile à utiliser, son fonctionnement interne doit être simple à comprendre, n'est-ce pas ? Ou vice versa ? En réalité, c'est tout le contraire. Si les deux concepts mènent spirituellement au même résultat, faire en sorte que quelque chose semble facile à l'extérieur nécessite une énorme complexité sous le capot.

Prenons l'exemple de Python, un langage connu pour sa faible barrière à l'entrée et, par conséquent, un choix favori comme langage de programmation d'entrée. Les écoles, les universités, les centres de recherche et un grand nombre d'entreprises à travers le monde ont choisi Python précisément parce qu'il est accessible à tous, quel que soit leur niveau d'éducation ou leur formation académique (ou leur absence totale). Il est rare que l'on ait besoin de beaucoup de théorie des types ou de comprendre comment et où les choses sont stockées dans la mémoire, sur quels threads un morceau de code s'exécute, etc. De plus, Python est la porte d'entrée vers certaines des bibliothèques scientifiques et de niveau système les plus profondes. Le fait de pouvoir contrôler une telle puissance avec une seule ligne de code est un argument de poids pour que Python devienne l'un des langages de programmation les plus populaires de la planète.


Et c'est là que le bât blesse : la facilité d'exprimer les choses en code Python a un coût. Sous le capot, l'interpréteur Python est massif, et de nombreuses opérations doivent être effectuées pour qu'une seule ligne de code soit exécutée. Lorsque vous entendez quelqu'un qualifier Python de langage "lent", une grande partie de la lenteur perçue provient du nombre de décisions prises par l'interpréteur au moment de l'exécution. Mais ce n'est même pas le plus gros problème, à mon avis. La complexité de l'écosystème d'exécution de Python, ainsi que certaines décisions de conception libérales concernant la gestion des paquets, constituent un environnement très fragile, et les mises à jour conduisent souvent à des incompatibilités et à des blocages de l'exécution. Il n'est pas rare de quitter une application Python pour y revenir quelques mois plus tard et se rendre compte que l'environnement hôte a suffisamment changé pour qu'il ne soit même plus possible de démarrer l'application.

Bien sûr, il s'agit d'une simplification grossière, et même les enfants de nos jours savent que les conteneurs existent pour résoudre des problèmes de ce type. En effet, grâce à Docker et à ses semblables, il est possible de "geler" les dépendances d'une base de code Python dans le temps afin qu'elle puisse pratiquement fonctionner éternellement. Cependant, cela se fait au prix d'un déplacement de la responsabilité et de la complexité vers l'infrastructure du système d'exploitation. Ce n'est pas la fin du monde, mais ce n'est pas non plus quelque chose à sous-estimer ou à négliger.

De la facilité à la simplicité

Si nous nous attaquions aux problèmes de Python, nous obtiendrions quelque chose comme Rust - extrêmement performant mais avec une barrière à l'entrée notoirement élevée. À mon avis, Rust n'est pas facile à utiliser et, qui plus est, il n'est pas simple. Bien qu'il fasse l'objet d'un engouement total ces jours-ci, malgré 20 ans de programmation et mes premiers pas en C et C++, je ne peux pas regarder un morceau de code Rust et dire avec certitude que je comprends ce qui s'y passe.

J'ai découvert Go il y a environ cinq ans alors que je travaillais sur un système basé sur Python. Bien qu'il m'ait fallu quelques essais pour me familiariser avec la syntaxe, j'ai immédiatement adhéré à l'idée de simplicité. Go est censé être simple à comprendre pour n'importe quel membre d'une organisation - du développeur junior fraîchement sorti de l'école au responsable technique de haut niveau qui ne regarde le code qu'occasionnellement. De plus, en tant que langage simple, Go reçoit très rarement des mises à jour syntaxiques - la dernière en date est l'ajout des génériques dans la version 1.18, qui n'est intervenu qu'après une décennie de discussions sérieuses. Dans l'ensemble, que vous regardiez du code Go écrit il y a cinq jours ou cinq ans, il est pratiquement identique et devrait fonctionner.

La simplicité exige cependant de la discipline. Elle peut sembler contraignante et même quelque peu rétrograde au début. Surtout si on la compare à une expression succincte, telle qu'une liste ou une compréhension de dictionnaire en Python :

Code : Sélectionner tout
1
2
3
4
5
temperatures = [
    {"city": "City1", "temp": 19},
    {"city": "City2", "temp": 22},
    {"city": "City3", "temp": 21},
]

filtered_temps = {
    entry["city"]: entry["temp"] for entry in temperatures if entry["temp"] > 20
}

Le même code en Go nécessite quelques frappes supplémentaires mais devrait idéalement être plus proche de ce que l'interpréteur Python fait sous le capot :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type CityTemperature struct {
    City      string
    Temp float64
}

// ...

temperatures := []CityTemperature{
    {"City1", 19},
    {"City2", 22},
    {"City3", 21},
}

filteredTemps := make(map[string]float64)
for _, ct := range temperatures {
    if ct.Temp > 20 {
        filteredTemps[ct.City] = ct.Temp
    }
}

Bien qu'il soit possible d'écrire un code équivalent en Python, une règle non écrite en programmation veut que si le langage offre une option plus facile (c'est-à-dire plus concise, plus élégante), les programmeurs graviteront autour de cette option. Mais la facilité est subjective, et la simplicité devrait s'appliquer de la même manière à tout le monde. La disponibilité d'alternatives pour effectuer la même action conduit à différents styles de programmation, et on peut souvent trouver plusieurs styles au sein d'une même base de code.

Go étant verbeux et "ennuyeux", il remplit naturellement une autre fonction : le compilateur Go a beaucoup moins de travail à faire lors de la compilation d'un exécutable. La compilation et l'exécution d'une application Go sont souvent aussi rapides, voire plus rapides, que le chargement de l'interpréteur Python ou de la machine virtuelle Java avant même l'exécution de l'application proprement dite. Il n'est pas surprenant qu'un exécutable natif soit aussi rapide qu'un exécutable peut l'être. Il n'est pas aussi rapide que ses homologues C/C++ ou Rust, mais avec une fraction de la complexité du code. Je suis prêt à négliger cet inconvénient mineur de Go. Enfin, les binaires Go sont statiquement liés, ce qui signifie que vous pouvez en construire un n'importe où et l'exécuter sur l'hôte cible - sans aucune dépendance de runtime ou de bibliothèque. Pour des raisons de commodité, nous enveloppons toujours nos applications Go dans des conteneurs Docker. Toutefois, ces derniers sont nettement plus petits et ne consomment qu'une fraction de la mémoire et de l'unité centrale de leurs homologues Python ou Java.

Comment nous utilisons Python et Go à notre avantage

La solution la plus pragmatique que nous avons trouvée dans notre travail est de combiner les pouvoirs de la facilité de Python et de la simplicité de Go. Pour nous, Python est un formidable terrain de jeu pour le prototypage. C'est là que les idées naissent et que les hypothèses scientifiques sont acceptées et rejetées. Python est naturellement adapté à la science des données et à l'apprentissage automatique, et comme nous travaillons beaucoup dans ce domaine, il n'est pas logique d'essayer de réinventer la roue avec quelque chose d'autre. Python est également au cœur de Django, ce qui témoigne de sa devise qui est de permettre le développement rapide d'applications comme peu d'autres outils (bien sûr, Ruby on Rails et Phoenix d'Elixir méritent d'être mentionnés ici).

Supposons qu'un projet ait besoin d'un minimum de gestion des utilisateurs et d'administration des données internes (comme c'est le cas pour la plupart de nos projets). Dans ce cas, nous commencerons par un squelette de Django en raison de son administration intégrée, qui est fantastique. Une fois que le prototype Django commence à ressembler à un produit, nous déterminons dans quelle mesure il peut être réécrit en Go. Comme l'application Django a déjà défini la structure de la base de données et l'aspect des modèles de données, il est assez facile d'écrire le code Go qui vient s'y ajouter. Après quelques itérations, nous parvenons à une symbiose, où les deux parties coexistent pacifiquement au-dessus de la même base de données et utilisent une messagerie simple pour communiquer l'une avec l'autre. Finalement, le "shell" Django devient un orchestrateur - il sert nos objectifs d'administration et déclenche des tâches qui sont ensuite gérées par son homologue Go. La partie Go s'occupe de tout le reste, depuis les API et les points d'accès frontaux jusqu'à la logique d'entreprise et le traitement des tâches en arrière-plan.

C'est une symbiose qui a bien fonctionné jusqu'à présent, et j'espère qu'il en sera ainsi à l'avenir.

Source : "Python is Easy. Go is Simple. Simple != Easy." par Preslav Rachev, ingénieur en informatique

Et vous ?

Quel est votre avis sur le sujet ?

Voir aussi

Go signe son retour dans le top 10 de l'indice de popularité des langages TIOBE après une absence de près de six ans. Python, C et Java conservent la tête du classement

Le créateur de Python, Guido van Rossum, dit ce qu'il pense de Rust, Go, Julia et TypeScript et relève des similitudes entre ces langages et Python

Go 1.21 apporte des améliorations au niveau du langage, de nouveaux paquets de bibliothèque standard, PGO GA, et une compatibilité ascendante et descendante dans la chaîne d'outils

Python 3.12 : la dernière version apporte des modifications du langage et de la bibliothèque standard, elle comporte aussi une amélioration des messages d'erreur

Une erreur dans cette actualité ? Signalez-nous-la !

Avatar de dexterity
Membre à l'essai https://www.developpez.com
Le 08/12/2023 à 9:15
Excellente analyse. Après 10 ans passés a développer en assembleur puis macro assembleur puis C, Pascal, VB, java, Ruby, Python, JavaScript... Je rencontre les mêmes problèmes avec Python et nodejs à savoir les dépendances... C'est infernal, malgré les gestionnaires d'environnement et docker. Cela dit, je préfère de loin ruby (et RAILS), c'est simple, beau, conci et maintenant plus rapide que python (testé dernièrement sur un algorithme de traitement d'un fichier de plus d'un demi milliard de ligne)
2  1 
Avatar de dexterity
Membre à l'essai https://www.developpez.com
Le 09/12/2023 à 8:03
temperatures = [ { :city => "Paris", :temp => 19}, { :city => "Lyon", :temp => 22}, { :city => "Marseille", :temp => 21} ]

filtered_temps = temperatures.map{|x| x if x[:temp] > 20}
2  1 
Avatar de Sve@r
Expert éminent sénior https://www.developpez.com
Le 12/12/2023 à 10:55
Bonjour
Citation Envoyé par dexterity  Voir le message
Cela dit, je préfère de loin ruby (et RAILS), c'est simple, beau, conci et maintenant plus rapide que python (testé dernièrement sur un algorithme de traitement d'un fichier de plus d'un demi milliard de ligne)

Je pourrais dire aussi que Python c'est simple, beau, concis et tenter ainsi d'imposer (de par l'expression "cela est") ma vérité. Toutefois je vais être plus honnête: je trouve moi Python personnellement simple, beau et concis.

Par ailleurs il est peut-être moins rapide que Ruby. Je ne vais même pas en débattre et l'accepter comme un fait acquis : il est moins rapide que Ruby concernant le traitement (on ne saura pas lequel) d'un fichier de plus de 500 millions de lignes (de quelles tailles les lignes... ne le précisons pas) et moins rapide dans quelle proportion, surtout ne le disons pas non plus !!!
Mais ça un dev python il n'en a rien à cirer car cela n'a jamais été son but !!! Python ne s'est jamais présenté comme étant le plus rapide pour ceci ou cela !!! Il m'est même arrivé de montrer à mes stagiaires (car j'enseigne Python) des codes comparatifs montrant que Python est moins rapide que le C (et même pas besoin d'un demi-milliard de lignes pour ça, un simple exemple de test de primalité d'un nombre ça le fait aussi) !!!
Mais encore une fois un dev python s'en balance car le but de Python n'a jamais été de se positionner comme le meilleur en quoi que ce soit (d'ailleurs, comme l'a dit Herbert Mayer, il n'y a pas de meilleur langage). Non, le but de Python a été de se positionner comme un langage souple et d'une syntaxe facile, rien d'autre. Et de mon point de vue il a réussi. Et ensuite, seulement ensuite, de pouvoir déléguer à d'autres technologies le soin de faire les traitements particuliers. Et vu les nombreuses passerelles qu'il existe depuis ces technologies vers Python, je pense là aussi qu'il a réussi (une fois la démo faite, je montre ensuite comment attaquer une fonction C depuis Python)
Donc celui (le seul, l'unique dev au monde) qui devra faire traiter son fichier de plus de 500 millions de lignes et qui voudra être plus rapide que Python (plus rapide de combien ça on ne le sait pas) passera par Ruby (grand bien lui fasse). Ensuite quelques nombreux autres qui devront écrire un OS ou un driver passeront par le C. Et enfin toute l'immense majorité des devs qui auront des travaux totalement éclectiques et variés (comme la gestion d'une chaine de montage, d'un magasin de supermarché, de données statistiques, etc etc etc) passeront par Python et ses passerelles.
https://sametmax2.com/python-meilleu...out/index.html

Quant à l'exemple de démo, heureusement que les noms des villes sont tous uniques sinon l'algo présenté ne fonctionnerait pas (on sent là l'exemple vraiment totalement crédible de ce qui se produit tous les jours dans la réalité)
Et en plus l'auteur de la solution Python ne passe même pas par les outils justement faits pour ce genre de cas...
Code python : Sélectionner tout
1
2
3
4
5
6
7
8
temperatures = ( 
	{"city": "Paris", "temp": 19}, 
	{"city": "Lyon", "temp": 22}, 
	{"city": "Marseille", "temp": 21}, 
)			# Inutile de mettre une liste quand un tuple suffit... 
  
filtered_temps = {entry["city"]: entry["temp"] for entry in filter(lambda x: x["temp"] > 20, temperatures)} 
print(filtered_temps)
Citation Envoyé par dexterity  Voir le message
Code ruby : Sélectionner tout
1
2
3
4
5
6
7
temperatures = [ 
  { :city => "Paris", :temp => 19}, 
  { :city => "Lyon", :temp => 22}, 
  { :city => "Marseille", :temp => 21} 
] 
  
filtered_temps = temperatures.map{|x| x if x[:temp] > 20}

Alors déjà l'exemple de démo renvoie au final un dictionnaire, tandis que ton code renvoie ce qui semble être une liste de dictionnaires ce qui n'a rien à voir (quand on compare des codes, on leur fait au-moins produire le même résultat) !!!
[nil, {:city=>"Lyon", :temp=>22}, {:city=>"Marseille", :temp=>21}]...
(ou alors je me trompe, ce qui est totalement possible vu que je ne connais pas Ruby)
1  0 
Avatar de Rep.Movs
Membre actif https://www.developpez.com
Le 28/12/2023 à 15:30
Franchement, que ce soit écrit en Go, Python, Lua, C#, Javascript avec un exemple aussi simpliste on ne peut pas voir la différence.
J'ai écrit ce genre d'algo en Fortran avec des fichiers séquentiels indexés -> à mon avis c'est encore plus efficace (simple) que Go.

Les problèmes de python ne se révèlent pas sur des petits programmes, mais sur de plus gros algo ou sur des interfaces techniques bas niveau. Là c'est la galère.
Et au niveau des dépendances, quand on s'aperçoit que la librairie qu'on utilise a une dépendance binaire liée à l'OS où à la CPU.

Dans tous les cas, le problème de performances dans la gestion de millions de lignes de texte est liée à plusieurs facteurs, essentiellement déclenchés par l'internationalisation, dont l'un est abstrait par la plupart des langages: le format de stockage et de traitement des chaînes de caractères.
Actuellement nos CPU sont ultra optimisées pour le calcul, mais le traitement de chaînes de caractères est devenu un enfer. Dans certains programme, plus de 50% du temps CPU correspond à de la manipulation de chaîne: stockage, traitement/mise en forme, comparaison (ultra discret mais diablement coûteux), hashage.

On ne stocke plus rien en ASCII. Utiliser de l'unicode qui utilise systématiquement 2 octets fait peser sur la mémoire. Et il faut stocker la chaîne si possible dans un format comparable (dans lequel séparer les accents des e est souvent une bonne idée) - mais au final si parfois on veut une comparaison "caractère par caractère", une comparaison "insensible à la casse", "insensible aux accents", "insensible aux caractères non imprimables" n'est pas une simple boucle avec une unique comparaison.
-> Là on a des implémentations différentes et des possibilités de gain.

Mention spéciale pour le float64 pour stocker une température dans un billet sur l'efficacité.
1  0