La concaténation en Python semble simple au premier regard, mais les bons choix changent vite dès qu’on passe d’un exemple court à un vrai code métier. Ici, je détaille les méthodes utiles pour assembler des chaînes et des listes, celles que je privilégie en pratique, et les pièges qui font perdre du temps dans un backend ou dans un script de transformation de données.
Les points essentiels à garder en tête
- Pour les chaînes,
''.join()est la référence dès qu’il faut assembler plusieurs fragments. - Pour deux morceaux de texte seulement,
+ou une f-string restent très lisibles. - Pour les listes,
+crée une nouvelle liste, tandis queextend()et+=modifient la liste existante. -
itertools.chain()est utile quand on veut parcourir plusieurs itérables sans les fusionner tout de suite. - Les erreurs les plus fréquentes viennent du mélange de types, des valeurs
Noneet des concaténations répétées dans une boucle.
Ce que la concaténation change selon le type de données
En Python, on ne concatène pas une chaîne et une liste de la même façon, parce que ces objets n’ont pas le même comportement interne. Les chaînes sont immuables : chaque nouvelle version est un nouvel objet. Les listes sont mutables : on peut les enrichir en place. Cette différence n’est pas un détail académique, elle détermine le choix de la syntaxe et, parfois, la lisibilité du code.
Avec une chaîne, l’opération produit toujours une nouvelle valeur. Avec une liste, on peut soit créer un nouveau résultat avec +, soit faire évoluer la structure existante avec extend() ou +=. Quand je travaille sur un flux de données ou une réponse API, je me pose d’abord une question simple: est-ce que je fabrique une valeur finale, ou est-ce que je construis progressivement une structure?
Cette logique explique aussi pourquoi certaines séquences ne se comportent pas comme on l’imagine. Toutes les collections n’acceptent pas la concaténation, et certaines, comme range, ont des règles plus strictes. Une fois ce cadre posé, on peut regarder la meilleure manière d’assembler des chaînes sans surcharger le code.
Concaténer des chaînes sans alourdir le code
Pour deux ou trois morceaux de texte, + reste parfaitement défendable. Dès que les fragments se multiplient, je préfère généralement ''.join(), parce que l’intention devient plus claire: on assemble une séquence de chaînes avec un séparateur donné, ici une chaîne vide. La documentation Python recommande d’ailleurs ''.join(sequence) comme manière rapide et propre de joindre plusieurs chaînes.
prenom = "Nora"
ville = "Lyon"
message = "Bonjour " + prenom + ", bienvenue à " + villeCe code fonctionne, mais il devient vite plus dense à relire si la phrase s’allonge. Dans ce cas, une f-string est souvent plus naturelle:
message = f"Bonjour {prenom}, bienvenue à {ville}"Et quand j’ai déjà une liste de fragments prêts à être assemblés, je passe volontiers par join:
morceaux = ["Bonjour ", prenom, ", bienvenue à ", ville]
message = "".join(morceaux)Je réserve surtout cette approche aux cas où l’on construit du texte généré: gabarits HTML, logs, messages d’erreur ou contenu à renvoyer depuis une API. La prochaine étape consiste à voir ce qui change du côté des listes, où la différence entre copier et modifier en place est beaucoup plus visible.
Assembler des listes proprement
Pour les listes, la vraie question n’est pas seulement “comment les joindre”, mais “que doit-il arriver à la liste d’origine”. a + b crée une nouvelle liste. a.extend(b) modifie a en place. Et a += b est, pour les listes, un raccourci pratique de extend(). En pratique, ce choix influence la lisibilité, mais aussi le comportement du code quand plusieurs références pointent vers la même liste.
| Méthode | Effet | Quand je la choisis |
|---|---|---|
a + b |
Crée une nouvelle liste | Quand je veux garder les deux listes intactes |
a.extend(b) |
Modifie a en place |
Quand je construis progressivement une collection |
a += b |
Équivaut à extend() pour les listes |
Quand la syntaxe courte reste claire dans le contexte |
itertools.chain(a, b) |
Retourne un itérateur, sans fusion immédiate | Quand je veux parcourir plusieurs sources sans créer de grosse structure intermédiaire |
Dans un projet backend, j’utilise souvent extend() quand je veux montrer clairement qu’une liste va absorber d’autres éléments. chain() me sert plutôt lorsque je n’ai pas besoin d’un nouvel objet tout de suite, par exemple pour traiter des sources successives. Avec ce cadre, il devient plus facile d’identifier les erreurs qui reviennent le plus souvent.
Les erreurs qui reviennent le plus souvent
La première erreur consiste à mélanger les types. Une chaîne et un entier ne se concatènent pas directement, donc il faut convertir avant d’assembler. C’est banal, mais c’est précisément le genre de détail qui casse un rendu HTML ou une réponse JSON au mauvais moment.
total = 42
texte = "Total: " + str(total)La deuxième erreur, c’est de laisser passer None dans une concaténation de chaînes. join() attend des chaînes, pas des valeurs hétérogènes. Si une donnée peut manquer, je préfère la filtrer explicitement ou la remplacer par une chaîne vide, plutôt que de laisser un bug silencieux se propager.
valeurs = ["Alice", None, "Paris"]
propres = [v for v in valeurs if v is not None]
resultat = " - ".join(propres)Troisième piège: croire qu’ajouter des listes “aplatit” leurs contenus. [[1], [2]] + [[3]] donne une liste de listes, pas [1, 2, 3]. Si l’objectif est d’aplatir, il faut une autre stratégie, par exemple une compréhension de liste ou chain() selon le volume et la lisibilité recherchée.
Je fais aussi attention à ne pas utiliser sum() pour assembler des chaînes: la solution recommandée reste join. Enfin, pour les bytes, on ne réutilise pas exactement les mêmes outils que pour les chaînes; la documentation Python oriente plutôt vers bytes.join() ou bytearray selon le besoin. Avec ces pièges en tête, le choix de la bonne technique devient beaucoup plus simple.

Choisir la bonne technique selon le contexte
Quand je dois trancher vite, je raisonne d’abord en fonction de l’intention. Est-ce que je fabrique une valeur finale, est-ce que j’enrichis une collection, ou est-ce que je parcours simplement plusieurs sources? Cette grille de lecture élimine la plupart des hésitations inutiles.
| Contexte | Choix le plus utile | Pourquoi |
|---|---|---|
| Deux ou trois morceaux de texte |
+ ou une f-string |
Le code reste court et immédiatement lisible |
| Beaucoup de fragments textuels | "".join(...) |
La construction est nette et plus prévisible |
| Fusionner deux listes ou plus |
extend() ou +=
|
On modifie la liste cible sans créer d’objet inutile |
| Parcourir plusieurs itérables | itertools.chain() |
On évite une fusion prématurée en mémoire |
| Générer un gabarit HTML ou une réponse texte | Collecte des morceaux puis join
|
Le rendu final est plus simple à maîtriser |
Dans un projet web, je reviens souvent à la même règle: je choisis la méthode la plus simple qui respecte le type de données et le cycle de vie de l’objet. Si je dois modifier une structure existante, je le fais en place; si je veux un résultat final, je le construis explicitement. Il reste un dernier réflexe utile pour éviter les mauvaises surprises quand le code grandit.
Le réflexe qui évite les mauvaises concaténations en production
Mon raccourci mental est simple: texte court avec + ou une f-string, texte long avec join, liste à enrichir avec extend, flux de données avec chain. Ce n’est pas une règle rigide, juste une manière fiable de réduire les erreurs et de garder un code compréhensible pour la suite.
Quand le contexte devient sensible, par exemple une génération de réponse API, un gabarit HTML ou un traitement de lots, je me pose toujours les mêmes questions: est-ce que je modifie l’objet, est-ce que je copie, est-ce que je diffère l’assemblage? Si la réponse n’est pas claire, c’est souvent le signe qu’une concaténation trop directe masque un choix de structure plus profond.
En pratique, la meilleure technique n’est presque jamais la plus astucieuse; c’est celle qui reste lisible, cohérente avec le type manipulé et facile à faire évoluer sans surprise.