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