In Python zijn tekenreeksen (str) onveranderlijke objecten, wat betekent dat je hun inhoud niet kunt wijzigen nadat ze zijn gemaakt. Elke bewerking die een tekenreeks wijzigt (zoals concatenatie of het vervangen van tekens) creëert een nieuw tekenreeksobject. Dit zorgt voor veiligheid en voorspelbaarheid, omdat je je geen zorgen hoeft te maken dat iemand de tekenreeks impliciet wijzigt in een andere plaats van de code.
s = 'Hallo' s2 = s.replace('H', 'J') # s blijft 'Hallo', en s2 wordt 'Jallo'
In tegenstelling tot tekenreeksen zijn lijsten in Python veranderlijke objecten. De inhoud kan ter plaatse worden gewijzigd via indexering of methoden, wat soms leidt tot impliciete effecten als dezelfde lijst op verschillende plaatsen wordt gebruikt.
Wat betreft prestaties: als je vaak grote tekenreeksen moet modificeren (bijvoorbeeld in een lus), kan de mechanica van onveranderlijkheid leiden tot overmatige geheugenallocatie en langzame code-uitvoering. In dergelijke gevallen wordt aanbevolen om een lijst te gebruiken voor het accumuleren van tekenreeksfragmenten en deze vervolgens samen te voegen met ''.join().
Voorbeeld:
# Slecht (langzaam voor grote hoeveelheden gegevens): s = '' for word in words: s += word # Op elke stap wordt er een nieuwe tekenreeks aangemaakt # Goed: parts = [] for word in words: parts.append(word) s = ''.join(parts)
Vraag: Waarom is de volgende code "s += 'abc'" sneller dan "s = s + 'abc'" voor tekenreeksen?
Antwoord: Dergelijke vragen worden gesteld om te controleren of iemand begrijpt dat beide bewerkingen eigenlijk gelijkwaardig zijn voor tekenreeksen (s += 'abc' creëert een nieuw object, net als s = s + 'abc') — zo is het gedrag van types in Python. Voor lijsten is het gedrag anders, omdat list += [...] het object muteert, terwijl list = list + [...] een nieuwe maakt. Voor tekenreeksen is het altijd een nieuwe tekenreeks.
s = 'hallo' s += 'abc' # Nieuw object, de oorspronkelijke tekenreeks verandert niet def compare(s): a = s a += 'abc' # id(a) != id(s) <-- verschillende objecten in geheugen
Verhaal
In een project waar verwerking van grote logboeken vereist was (parseren van tekenreeksen van honderden megabytes), gebruikte de ontwikkelaar naïeve concatenatie van tekenreeksen in een lus. Het resultaat — enorme dalingen in prestaties en een snelle toename van geheugengebruik. Na optimalisatie met een lijst en join() werd de uitvoeringstijd met 20 keer verminderd.
Verhaal
In een project, toen hij probeerde een teken in een tekenreeks te "corrigeren" op index, verwachtte de programmeur de wijziging van de oorspronkelijke tekenreeks te zien. Er deed zich een fout voor: TypeError: 'str' object does not support item assignment. Na enkele uren besteden, moest de debugger een nieuwe tekenreeks maken met behulp van slicen en het gewenste teken vervangen.
Verhaal
Bij het doorgeven van tekenreeksen aan een functie om ze te "aanvullen" (bijvoorbeeld het toevoegen van een suffix aan elk element van een lijst), verwachtte een van de ontwikkelaars dat de tekenreeks "op locatie" zou veranderen. Het resultaat — de functie gaf None terug (door het ontbreken van return), en alle tekenreeksen bleven origineel.