Python 中的可變與不可變

Python 中的可變與不可變

本文將解釋 Python 中的可變與不可變。

YouTube Video

Python 中的可變與不可變

「可變」和「不可變」是指物件的可變性。理解這一點有助於避免意外的錯誤提高內存管理效率

什麼是可變的?

可變物件可以在創建後更改其內部狀態

主要的可變數據類型

  • list
  • dict
  • set
  • 使用者定義的類別(如果其屬性可被修改)

範例:修改列表

1numbers = [1, 2, 3]
2numbers[0] = 100
3print(numbers)  # [100, 2, 3]

列表是一個可變物件,其元素可以自由修改。

什麼是不可變的?

不可變物件在創建後無法被更改。嘗試修改它們將導致創建一個新物件。

主要的不可變數據類型

  • int
  • float
  • str
  • tuple
  • bool
  • frozenset

範例:修改字串

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"

字串是不可變的,因此無法部分修改它們。

可變與不可變的比較

 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

從這個例子可以看到,可變物件是以引用來共享的,所以它們可以影響到其他變數。另一方面,不可變物件在重新賦值時會創建新的實例,保持原始值不受影響

使用 id() 檢查內部行為

在 Python 中,你可以使用 id() 函數來檢查物件的 ID。物件的 ID 類似於記憶體位址。

 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

如圖所示,不可變類型會創建新的對象,而可變類型則會就地修改

函數與可變和不可變物件的注意事項

當將可變物件傳遞給函數時,原始數據可能會被修改

範例:修改列表的函數

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]

範例:修改數字的函數

另一方面,嘗試修改不可變物件會導致創建一個新的物件。

1def modify_number(n):
2    n += 10
3
4my_number = 5
5modify_number(my_number)
6print(my_number)  # 5 -> unchanged

實用考量

避免將可變物件用作默認參數

 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]

由於默認參數在函數定義時只評估一次,使用可變物件可能會導致意想不到的副作用。

  • 在第一個例子中,每次呼叫 add_item 都會使用同一個列表物件。當第二次呼叫 add_item(2) 時,先前加入的 1 仍在列表中,因此結果是 [1, 2]
  • 在改進後的例子中,將 None 作為預設值,如果參數是 None,就會在函式內建立一個新的列表。這樣可以確保每次呼叫函式時都會建立一個新的列表,因此以前的結果不會影響後續的呼叫。

避免使用像列表或字典一樣的可變物件作為預設參數,應該使用 None 並在函式內部初始化它們。這是在 Python 中一項基本且重要的最佳實踐。

總結

要深入了解 Python 的變數和數據類型,掌握可變和不可變之間的區別至關重要。理解這些特性可以幫助你避免程式中的非預期行為,並撰寫更健壯且可讀性更高的程式。

您可以在我們的 YouTube 頻道上使用 Visual Studio Code 來跟隨上述文章一起學習。 請也查看我們的 YouTube 頻道。

YouTube Video