Typy w pythonie dzielą się na dwa fundamentalne kategorie - mutowalne oraz na niemutowalne. Zrozumienie o co z tym chodzi zaoszczędzi ci wiele problemów i jeszcze więcej bugów. Na wstępie pragnę zaznaczyć, że aby odnaleźć się w tym artykule potrzeba wiedzieć czym jest funkcja oraz pętla. Jeśli jeszcze tego nie wiesz to polecam poczekać do jutra na mój wpis o tych rzeczach i dopiero po nim wrócić tutaj.
Mutowalność
Mutowalność w skrócie to możliwość modyfikacji danej wartości. Może się to wydawać dziwne, ja szczerze mówiąc na początku myślałem, że wszystkie typy są mutowalne bo przecież można zmienić dowolnie zmienną - całkowicie błędne myślenie. Najprościej będzie mi to omówić na podstawie przykładu; napisałem prostą funkcję, która modyfikuje podany string dodając do niego "a":
def zmien_string(string):
return string + "a"
string = "slow"
zmien_string(string)
zmien_string(string)
zmien_string(string)
Teraz pojawia się pytanie jaką wartość będzie miał string string po potrójnym wykonaniu funkcji zmien_string. Czy to będzie "slowaaa"? Nie, to będzie "slow". Dzieje się tak dlatego, ponieważ string należy do niemutowalnego typu, którego nie da się zmienić. Operacja string + "a"
w rzeczywistości przyczynia się do powstania nowego stringu, który zostaje zwrócony w funkcji, ale nie zostaje przypisany do żadnej zmiennej, więc nie można się do niego odnieść. Oczywiście ta nowa wartość jest gdzieś alokowana w pamięci i pythonowy "sprzątacz śmieci" będzie musiał z czasem się tego pozbyć. Z tego powodu bardzo niewskazane są rozwiązania tego typu
s = ""
for i in range(10):
s = s + "a"
ponieważ gdzieś w pamięci są przechowywane te jedenaście stringów nawet jeśli są przypisane do tej samej zmiennej.
Teraz weźmy na warsztat podobny przykład, ale z mutowalnym typem jak lista. Tworzymy funkcję zmien_liste, która dodaje do listy, na sam koniec, jeden element i jest to jedynka (integer)
l = []
def zmien_liste(lista):
return lista.append(1)
zmien_liste(l)
zmien_liste(l)
zmien_liste(l)
Zmienna l będzie miała wartość [1, 1, 1]
.
Typy niemutowalne | Typy mutowalne |
---|---|
int | list |
float | dict |
complex | bytearray |
bool | obiekty użytkownika |
string | |
tuple | |
range | |
frozenset | |
bytes |
Jak widzicie tych typów jest trochę więcej niż omawiałem to w poprzednim artykule. Ważne żeby znać krotke czyli tuple, która jest niemutowalnym odpowiednikiem listy, oraz decimal - typ zmiennoprzecinkowy o wysokiej precyzji używany w finansach do obliczeń arytmetycznych bo mówiąc potocznie nie gubi końcówek.
Bugi
Najczęstszy błąd związany z mutowalnością to przyjęcie mutowalnej wartości jako argument funkcji/metody, poddanie jej modyfikacji a następnie zwrócenie z myślą, że to już całkiem nowa wartość a ta stara zdefiniowana gdzieś na zewnątrz nie została w żaden sposób tknięta. W django istnieje pole "JSONField", które jako domyślną wartość może przyjąć słownik, niestety jeśli zrobi się to źle (czyli JSONField(default={})
) to obiekty będą miały współdzieloną przestrzeń - obiekty zamiast posiadać własne oddzielne słowniki w polu będą współdzieliły jeden duży - co dość, że doprowadzi do poważnego buga to jeszcze w znaczącym stopniu narusza bezpieczeństwo. Mi się zdarza często zapominać, że QuerySety w django są niemutowalne i nie wiem co się dzieje gdy dokładam kolejny filter a nic się nie zmienia.
Podsumowanie
Omówiłem pokrótce mutowalność a jutro przerobimy pętle, instrukcje warunkowe oraz funkcje.
Poprzednio:
Jeśli ci się podobało i chcesz więcej to upvote i dodanie mnie do obserwowanych zawsze będzie dodatkową zachętą do pracy 😉