转:python可变类型与不可变类型

一、核心区别

1. 不可变类型(immutable)

值一旦创建就不能原地修改,如果重新赋值、修改内部数据,会直接开辟一块新内存地址,变量指向新地址。
 
原内存里的数据不会被改动。
常见不可变类型:
 
int、float、str、bool、tuple(元组)、frozenset

2. 可变类型(mutable)

可以在原内存地址上直接修改内部元素,内存地址不变,只是里面的数据变了。
 
常见可变类型:
 
list(列表)、dict(字典)、set(集合)

二、详细举例说明

1. 不可变类型示例:字符串 str

python
 
运行
 
 
 
s = "hello"
# 字符串不能原地修改单个字符
# s[0] = "H"  # 报错!TypeError

# 只能重新赋值,指向新地址
s = "Hello"
 

2. 不可变类型:元组 tuple

元组本身不可变,但如果元组里嵌套了可变对象,可以修改里面可变对象的内容:
python
 
运行
 
 
 
t = (1, 2, [3, 4])
# t[0] = 100  # 报错,元组元素不能替换
t[2].append(5)
print(t)  # (1, 2, [3, 4, 5])
 

3. 可变类型:列表 list

原地修改,内存地址不变:
python
 
运行
 
 
 
lst = [1,2,3]
lst.append(4)
lst[0] = 10
# 变量指向的内存地址没变
 

4. 字典 dict、集合 set

同样是原地修改,属于可变类型。

三、id () 验证内存地址

id(变量):查看变量内存地址

不可变类型

python
 
运行
 
 
 
a = 10
print(id(a))
a = 20
print(id(a))  # 地址变了,开辟新空间
 

可变类型

python
 
运行
 
 
 
lst = [1,2]
print(id(lst))
lst.append(3)
print(id(lst))  # 地址不变,原地修改
 

四、关键使用场景考点

1. 函数传参

  • 不可变类型传参:函数内修改不会影响外部变量(值传递效果)
  • 可变类型传参:函数内原地修改会影响外部原对象(引用传递)
python
 
运行
 
 
 
def func(lst):
    lst.append(999)

my_list = [1,2]
func(my_list)
print(my_list)  # [1,2,999] 外部被修改
 

2. 字典的 key 要求

字典的 key 必须是不可变类型
 
list、dict、set 不能当 key;int/str/tuple 可以。

3. 默认参数陷阱(面试高频)

默认参数只在函数定义时初始化一次,尽量不要用可变类型当默认参数:
python
 
运行
 
 
 
def func(x=[]):
    x.append(1)
    print(x)

func()  # [1]
func()  # [1,1] 不是预期的[1]
 
原因:默认参数列表只创建一次,后续调用一直复用同一个列表对象。

五、分类速记表

表格
 
类型是否可变特点
int、float、bool、str、tuple、frozenset 不可变 不能原地修改,修改就换内存地址
list、dict、set 可变 可原地增删改,内存地址不变

六、易错点总结

  1. 元组不可变 ≠ 元组里的元素不能变,只是元素引用不能替换
  2. 只有不可变类型可以哈希(hashable),能作为字典 key、存入 set
  3. 可变对象作为函数默认参数会有共享陷阱

七、基础选择题(5 道)

  1. 以下属于不可变类型的是()
     
    A. list B. tuple C. dict D. set
     
    答案:B
     
    解析:元组不可变,列表、字典、集合都是可变类型。
  2. 下列哪种类型不能作为字典的 key()
     
    A. int B. str C. list D. tuple
     
    答案:C
     
    解析:字典 key 必须可哈希(不可变),列表可变不可哈希,不能做 key。
  3. 关于元组t = (1, [2,3]),下列说法正确的是()
     
    A. t [1] = [4,5] 可以执行
     
    B. t [1].append (4) 可以执行
     
    C. 元组内所有元素都不能修改
     
    D. 元组属于可变类型
     
    答案:B
     
    解析:元组约束的是元素引用不能替换,内部嵌套的列表可以原地修改。
  4. 执行以下代码,两次 id (a) 结果()
python
 
运行
 
 
 
a = 10
a = a + 5
 
A. 相同 B. 不同 C. 不确定
 
答案:B
 
解析:int 不可变,运算后生成新对象,内存地址改变。
  1. 以下代码执行后输出结果是()
python
 
运行
 
 
 
lst = [1,2,3]
lst2 = lst
lst2.append(4)
print(lst)
 
A. [1,2,3] B. [1,2,3,4] C. 报错
 
答案:B
 
解析:lst 和 lst2 指向同一个列表对象,原地修改互相影响。

八、代码输出题(高频必考 8 道)

题 1:int 不可变传参

python
 
运行
 
 
 
def test(num):
    num = 100
    print(num)

a = 10
test(a)
print(a)
 
输出:
plaintext
 
 
100
10
 
解析:int 不可变,函数内只是新建局部变量,不会修改外部全局 a。

题 2:list 可变传参(高频)

python
 
运行
 
 
 
def test(lst):
    lst.append(100)

arr = [1,2]
test(arr)
print(arr)
 
输出:[1, 2, 100]
 
解析:列表是可变对象,函数内原地修改,外部原列表同步变化。

题 3:函数内重新赋值可变对象(易错坑)

python
 
运行
 
 
 
def test(lst):
    lst = [10,20]
    lst.append(30)

arr = [1,2]
test(arr)
print(arr)
 
输出:[1, 2]
 
解析:函数内lst = [10,20]是新建局部列表,不再指向外部 arr,不会修改原列表。

题 4:默认参数经典陷阱(面试必问)

python
 
运行
 
 
 
def func(x=[]):
    x.append(1)
    print(x)

func()
func()
func()
 
输出:
plaintext
 
 
[1]
[1, 1]
[1, 1, 1]
 
解析:默认参数只在函数定义时创建 1 次,后续调用共用同一个列表对象。
 
✅ 正确写法:
python
 
运行
 
 
 
def func(x=None):
    if x is None:
        x = []
    x.append(1)
    print(x)
 

题 5:字符串不可变

python
 
运行
 
 
 
s = "python"
# s[0] = "P"
print(s)
 
输出:直接抛出 TypeError
 
解析:字符串不支持原地修改单个字符,只能重新赋值。

题 6:元组嵌套可变对象

python
 
运行
 
 
 
t = (1, 2, [3, 4])
t[2].append(5)
print(t)
 
输出:(1, 2, [3, 4, 5])

题 7:浅拷贝赋值对比

python
 
运行
 
 
 
a = [1,2]
b = a
c = a.copy()
b.append(3)
print(a, c)
 
输出:[1, 2, 3] [1, 2]

题 8:哈希判断

python
 
运行
 
 
 
print(hash(123))
print(hash("abc"))
# print(hash([1,2]))
 
列表调用 hash 会直接报错,可变类型不可哈希。

九、简答题(面试口述题)

  1. 简述可变类型和不可变类型的区别?
     
    答:
     
    不可变类型:值不能原地修改,修改会生成新对象,内存地址改变,可哈希,能作为字典 key;
     
    可变类型:支持原地增删改,内存地址不变,不可哈希,不能作为字典 key。
  2. 为什么不推荐用可变类型作为函数默认参数?怎么解决?
     
    答:函数定义阶段默认参数只初始化一次,多次调用会复用同一个可变对象,导致数据错乱;
     
    解决:默认参数设为 None,函数内部再初始化可变对象。
  3. 元组是不可变类型,为什么元组里可以放列表并且修改列表内容?
     
    答:元组保存的是元素的内存引用地址,不允许修改引用指向;列表本身可变,只是修改列表内部数据,没有改变元组存储的引用地址,所以合法。

十、进阶思考题

python
 
运行
 
 
 
a = (1, [2])
b = a
a[1].append(3)
print(b)
 
输出:(1, [2, 3])
 
解析:a、b 指向同一个元组,内部列表原地修改,两个变量看到的数据都会变化。
posted @ 2020-05-28 10:58  sjxm2017  阅读(220)  评论(0)    收藏  举报