W Pythonie łańcuchy (str) są obiektami niemutowalnymi, co oznacza, że po utworzeniu nie można zmienić ich zawartości. Każda operacja, która modyfikuje łańcuch (na przykład konkatenacja lub zastępowanie znaków), tworzy nowy obiekt łańcucha. Zapewnia to bezpieczeństwo i przewidywalność działania, ponieważ nie musisz się martwić, że ktoś niejawnie zmieni łańcuch w innej części kodu.
s = 'Hello' s2 = s.replace('H', 'J') # s pozostaje 'Hello', a s2 będzie 'Jello'
W przeciwieństwie do łańcuchów, listy w Pythonie są obiektami mutowalnymi. Ich zawartość można zmieniać na miejscu poprzez indeksację lub metody, co czasami prowadzi do niejawnych efektów, jeśli ten sam list jest używany w różnych miejscach.
Z punktu widzenia wydajności: jeśli musisz często modyfikować duże łańcuchy (na przykład w pętli), mechanika niemutowalności może prowadzić do nadmiernej alokacji pamięci i wolnego działania kodu. W takich przypadkach zaleca się użycie listy do gromadzenia fragmentów łańcucha, a następnie łączenie ich za pomocą ''.join().
Przykład:
# Źle (wolno dla dużych zbiorów danych): s = '' for word in words: s += word # Na każdym kroku tworzony jest nowy łańcuch # Dobrze: parts = [] for word in words: parts.append(word) s = ''.join(parts)
Pytanie: Dlaczego następujący kod "s += 'abc'" jest szybszy niż "s = s + 'abc'" dla łańcuchów?
Odpowiedź: Takie pytania są zadawane, aby sprawdzić, czy osoba rozumie, że obie operacje są w rzeczywistości równoważne dla łańcuchów (s += 'abc' tworzy nowy obiekt, tak jak s = s + 'abc') — tak działa zachowanie typów w Pythonie. Dla list zachowanie jest inne, ponieważ list += [...] mutuje obiekt, a list = list + [...] tworzy nowy. Dla łańcuchów to zawsze nowy łańcuch.
s = 'hi' s += 'abc' # Nowy obiekt, oryginalny łańcuch nie zmienia się def compare(s): a = s a += 'abc' # id(a) != id(s) <-- różne obiekty w pamięci
Historia
W projekcie, gdzie wymagano przetwarzania dużych logów (analiza łańcuchów o długości setek megabajtów), programista używał naiwnych konkatenacji łańcuchów w pętli. Wynik — ogromne spadki wydajności i szybki wzrost zużycia pamięci. Po optymalizacji poprzez listę i join() czas wykonania zmniejszył się 20 razy.
Historia
W jednym projekcie, podczas próby "naprawienia" znaku w łańcuchu na podstawie indeksu, programista oczekiwał, że zobaczy zmianę oryginalnego łańcucha. Pojawił się błąd TypeError: 'str' object does not support item assignment. Po kilku godzinach, debugger musiał utworzyć nowy łańcuch za pomocą wycinków i zastąpić potrzebny znak.
Historia
Podczas przekazywania łańcuchów do funkcji w celu ich "uzupełnienia" (na przykład, aby dodać sufiks do każdego elementu z listy), jeden z programistów oczekiwał, że łańcuch zmieni się "na miejscu". Wynik — funkcja zwracała None (z powodu braku return), a wszystkie łańcuchy pozostały oryginalne.