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