Zmienialne i niezmienialne w Pythonie
Ten artykuł wyjaśnia zmienialność i niezmienialność w Pythonie.
YouTube Video
Zmienialne i niezmienialne w Pythonie
"Zmienialne" i "niezmienialne" odnoszą się do zdolności obiektu do zmiany stanu. Zrozumienie tego pomaga unikać nieoczekiwanych błędów i efektywnie zarządzać pamięcią.
Co to jest Zmienialne?
Zmienialne obiekty mogą zmieniać swój stan wewnętrzny po utworzeniu.
Główne zmienialne typy danych
list
dict
set
- Klasy definiowane przez użytkownika (jeśli ich atrybuty mogą być modyfikowane)
Przykład: Modyfikacja listy
1numbers = [1, 2, 3]
2numbers[0] = 100
3print(numbers) # [100, 2, 3]
Lista jest obiektem zmienialnym, a jej elementy mogą być dowolnie modyfikowane.
Co to jest Niezmienialne?
Niezmienialne obiekty nie mogą być zmieniane po ich utworzeniu. Próba ich zmiany skutkuje utworzeniem nowego obiektu.
Główne niezmienialne typy danych
int
float
str
tuple
bool
frozenset
Przykład: Modyfikacja napisu
1text = "hello"
2# text[0] = "H" # TypeError: 'str' object does not support item assignment
3
4text = "H" + text[1:] # Creates a new string
5print(text) # "Hello"
Napisy są niezmienialne, więc nie można ich częściowo modyfikować.
Porównanie zmienialnego i niezmienialnego
1# Mutable example
2a = [1, 2, 3]
3b = a
4b[0] = 100
5print(a) # [100, 2, 3] -> a is also changed
6
7# Immutable example
8x = 10
9y = x
10y = 20
11print(x) # 10 -> x is unchanged
Jak widać na tym przykładzie, obiekty mutowalne są przekazywane przez referencję, dlatego mogą wpływać na inne zmienne. Z drugiej strony, obiekty niemutowalne tworzą nowe instancje po przypisaniu, pozostawiając pierwotną wartość nienaruszoną.
Analizowanie wewnętrznego zachowania za pomocą id()
W Pythonie możesz użyć funkcji id()
, aby sprawdzić identyfikator obiektu. Identyfikator obiektu jest podobny do adresu pamięci.
1# Immutable int behavior
2a = 10
3print(id(a)) # e.g., 140715920176592
4a += 1
5print(id(a)) # e.g., 140715920176624 -> ID has changed
6
7# Mutable list behavior
8b = [1, 2, 3]
9print(id(b)) # e.g., 2819127951552
10b.append(4)
11print(id(b)) # Same ID -> only the content has changed
Jak pokazano, dla typów niemutowalnych tworzony jest nowy obiekt, podczas gdy typy mutowalne są modyfikowane w miejscu.
Funkcje i ostrożność przy obiektach mutowalnych i niemutowalnych
Przy przekazywaniu obiektu mutowalnego do funkcji, oryginalne dane mogą zostać zmodyfikowane.
Przykład: Funkcja modyfikująca listę
1def modify_list(lst):
2 lst.append(100)
3
4my_list = [1, 2, 3]
5modify_list(my_list)
6print(my_list) # [1, 2, 3, 100]
Przykład: Funkcja modyfikująca liczbę
Z drugiej strony, próba modyfikacji obiektu niemutowalnego skutkuje utworzeniem nowego obiektu.
1def modify_number(n):
2 n += 10
3
4my_number = 5
5modify_number(my_number)
6print(my_number) # 5 -> unchanged
Rozważania praktyczne
Unikaj używania mutowalnych obiektów jako domyślnych argumentów
1# Bad example
2def add_item(item, container=[]):
3 container.append(item)
4 return container
5
6print(add_item(1)) # [1]
7print(add_item(2)) # [1, 2] -> unintended behavior
8
9# Good example
10def add_item(item, container=None):
11 if container is None:
12 container = []
13 container.append(item)
14 return container
15
16print(add_item(1)) # [1]
17print(add_item(2)) # [2]
Ponieważ domyślne argumenty są oceniane tylko raz podczas definiowania funkcji, używanie obiektów mutowalnych może prowadzić do nieoczekiwanych skutków ubocznych.
- W pierwszym przykładzie ten sam obiekt listy jest używany za każdym razem, gdy wywoływana jest funkcja
add_item
. Podczas drugiego wywołaniaadd_item(2)
wcześniej dodana liczba1
wciąż znajduje się na liście, co daje wynik[1, 2]
. - W ulepszonym przykładzie
None
jest używane jako domyślna wartość, a nowa lista jest tworzona wewnątrz funkcji, jeśli argument jest równyNone
. Dzięki temu nowa lista tworzona jest przy każdym wywołaniu funkcji, więc poprzednie wyniki nie wpływają na kolejne wywołania.
Należy unikać używania obiektów mutowalnych, takich jak listy lub słowniki, jako domyślnych argumentów; zamiast tego należy użyć None
i zainicjować je wewnątrz funkcji. To fundamentalna i bardzo ważna dobra praktyka w Pythonie.
Podsumowanie
Aby dogłębnie zrozumieć zmienne i typy danych w Pythonie, kluczowe jest zrozumienie różnic między mutowalnymi a niemutowalnymi. Zrozumienie tych cech pomaga unikać niezamierzonych zachowań w Twoim kodzie oraz pisać bardziej solidne i czytelne programy.
Możesz śledzić ten artykuł, korzystając z Visual Studio Code na naszym kanale YouTube. Proszę również sprawdzić nasz kanał YouTube.