Day Three: Mutowalność w pythonie

in #polish7 years ago (edited)

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 niemutowalneTypy mutowalne
intlist
floatdict
complexbytearray
boolobiekty 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 😉