可变性
- 可变对象:可以在对象创建后修改其内容(值)。
- 不可变对象:对象一旦创建后,其内容不能再被修改。
常见的可变和不可变类型:
- 可变对象:
- 列表 (
list
) - 字典 (
dict
) - 集合 (
set
) - 用户自定义的类实例(默认情况下)
- 列表 (
- 不可变对象:
- 整数 (
int
) - 浮点数 (
float
) - 字符串 (
str
) - 元组 (
tuple
) - 布尔值 (
bool
) - frozenset (
frozenset
)
- 整数 (
例子:
# 可变对象:列表
lst = [1, 2, 3]
lst[0] = 100 # 修改成功
# 不可变对象:元组
tup = (1, 2, 3)
# tup[0] = 100 # 这一行会报错,因为元组不可修改
与引用的关系:
- 可变对象通过引用传递时,如果外部函数对对象进行修改,则在原作用域中对象的内容也会改变。
- 不可变对象通过引用传递时,由于内容不能更改,传递的是一个新的对象引用。
是否可哈希
- 可哈希对象:对象具有一个固定的哈希值,可以通过调用
hash()
函数来获得,并且它们的内容在对象生命周期内不能改变。- 不可变对象:
int
、float
、str
、tuple
(如果所有元素都是可哈希的)、frozenset
。 - 这些对象通常可以作为 字典的键 或 集合的元素。
- 不可变对象:
- 不可哈希对象:没有哈希值,通常因为对象是可变的,其内容可能随时更改,从而使哈希值不再一致。
- 可变对象:
list
、dict
、set
。 - 这些对象不能作为 字典的键 或 集合的元素。
- 可变对象:
# 可哈希对象
print(hash("hello")) # 字符串是不可变的,可哈希
print(hash((1, 2, 3))) # 元组是不可变的,可哈希
# 不可哈希对象
# print(hash([1, 2, 3])) # 列表是可变的,无法哈希
# print(hash({'a': 1})) # 字典是可变的,无法哈希
# print(hash({1, 2, 3})) # 集合是可变的,无法哈希
哈希性与可变性的关系:
- 可哈希对象通常是不可变对象,因为只有不可变对象才能保证哈希值在其生命周期内不会改变。
- 不可哈希对象往往是可变对象,因为其内容可以改变,哈希值会随之发生变化。
Python 的参数传递机制
Python 中的参数传递既不是值传递也不是引用传递,而是对象的引用传递。这意味着函数接收到的是对象的引用,而不是对象本身的拷贝。因此,如果对象是可变的,修改它会影响到调用者范围内的对象。
具体的表现可以根据对象的可变性来区分:
- 可变对象:当你将可变对象(如
list
、dict
、set
)作为参数传递到函数时,函数内部的修改会影响到外部的变量,因为函数操作的是对象的引用,即它们指向同一个对象。 - 不可变对象:当你将不可变对象(如
int
、float
、str
、tuple
、bool
、frozenset
)作为参数传递到函数时,任何修改都会创建一个新的对象,函数内部的修改不会影响外部的变量。
可变对象和不可变对象在引用传递中的表现
# 可变对象(列表)
def modify_list(lst):
lst.append(4)
my_list = [1, 2, 3]
modify_list(my_list)
print(my_list) # 输出: [1, 2, 3, 4]
在这个例子中,my_list
是一个可变对象(列表)。当我们把它传递给 modify_list
函数时,函数内部对列表的修改(append
操作)会直接影响外部的 my_list
,因为它们引用的是同一个对象。
# 可变对象(字典)
def modify_dict(d):
d['new_key'] = 'new_value'
my_dict = {'key': 'value'}
modify_dict(my_dict)
print(my_dict) # 输出: {'key': 'value', 'new_key': 'new_value'}
这里的 my_dict
是一个字典(可变对象),传递到函数 modify_dict
后,函数内部对字典的修改直接反映在外部的 my_dict
上。
# 不可变对象(字符串)
def modify_string(s):
s = "new string" # 试图修改传入的字符串
my_str = "old string"
modify_string(my_str)
print(my_str) # 输出: "old string"
这里的 my_str
是一个不可变对象(字符串)。当我们把它传递给 modify_string
函数时,函数内部的赋值操作创建了一个新的字符串对象,my_str
本身并没有被修改,外部的变量仍然保持原样。
# 不可变对象(整数)
def modify_number(n):
n += 10 # 修改传入的数值
my_num = 5
modify_number(my_num)
print(my_num) # 输出: 5
my_num
是一个不可变对象(整数)。即使在 modify_number
函数内部对其进行了修改,这也不会影响外部的 my_num
。在函数内部,修改操作实际上生成了一个新的整数对象,而原来的 my_num
仍指向原来的值。
总结
可变性直接决定了对象是否可以被哈希。如果对象是可变的,它的值可以在生命周期中改变,导致哈希值也不固定,因此可变对象不可哈希。
可哈希对象通常是不可变的,它们具有稳定的哈希值,并且可以用作 字典的键 或 集合的元素。
引用使得多个变量可以指向同一个对象。对于可变对象,多个引用之间会互相影响。而对于不可变对象,修改操作实际上是在创建一个新的对象,原有的引用保持不变。
实际工作中:
哈希性的要求决定了对象的使用场景。例如,字典的键和集合的元素必须是可哈希的对象,所以必须是不可变的。
可变对象可以通过引用进行修改,因此在编写代码时要小心引用的共享,避免意外的修改。如果不希望对象被意外修改,考虑使用不可变对象或创建对象副本。