Zmienialne i niezmienialne w Pythonie

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łania add_item(2) wcześniej dodana liczba 1 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ówny None. 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.

YouTube Video