为什么 Python 默认参数必须用不可变对象?

话不多说,上代码:

def fun(x, A=[], B=''):
    C = []
    A.append(x)
    B = B + x
    C.append(x)
    print(f"A:{A} B:{B} C:{C}")

fun('1')
fun('2')
fun('3')

我们在这段代码中:

定义了一个位置参数 x,一个一个默认参数 A,A 的默认值为空列表,另一个默认参数 B,B 的默认值为一个空字符串,在函数体中定义了一个变量 C,并也给 C 传递了一个空列表

大家猜猜函数执行会输出怎样一个结果?

# 为了排版工整略微修改了一下输出样式
A:['1']               B:1     C:['1']
A:['1', '2']          B:2     C:['2']
A:['1', '2', '3']     B:3     C:['3']

大家猜到了这个结果了吗?猜到的朋友都是大佬,下面的内容就可以略过了。没有猜到的朋友也不要慌,毕竟这玩意儿有点坑。。。

在解释这个结果之前,大家需要复习一下 Python 中可变对象和不可变对象这两个概念:

  • 可变对象:对象指向的内存中的值会改变,当更改这个变量的时候,还是指向原来内存中的值,并且在原来的内存值进行原地修改,并没有开辟新的内存(list、dict、set);
  • 不可变对象:对象所指向的内存中的值不能被改变,当改变这个变量的时候,原来指向的内存中的值不变,变量不再指向原来的值,而是开辟一块新的内存,变量指向新的内存(int、float、str、tuple、bool、None)。

概念复习完了就开始进入正题了:

在Python程序中,函数在被定义创建时,Python就会为默认参数分配一块儿空间。在这个程序中,为默认变量 A 分配了一个内存地址,其中的值为一个空列表,为 B 也分配了一块儿空间,值为空字符串:

地址:0x0001   值:[] ==> A
地址:0x0002   值:'' ==> B

Python 在调用函数时,会直接将在函数定义时得到的内存地址复制给默认参数!

因此会在调用函数时出现下面的内存变化(内存地址为了方便看才排列这样有序的,实际程序里不可能这样):

当第一次调用函数时

# 调用开始时: A指向0x0001这个地址,B指向0x0002这个地址
C = []      	# 对象C指向一个空列表
A.append(x)		# 对象A指向的空列表(地址:0x0001)中添加了'1'
B = B + x		# 开辟了一块儿新内存,其中值为'1',B不再指向''(地址:0x0002)而指向'1'
C.append(x)		# 对象C指向的空列表中添加了'1'
print(f"A:{A} B:{B} C:{C}") # A:['1']  B:'1'  C:['1']

函数执行完后内存指向:

地址:0x0001   值:['1'] ==> A
地址:0x0002   值:''
地址:0x0003   值:['1'] ==> C
地址:0x0004   值:'1'   ==> B

当第二次调用函数时

# 调用开始时: A指向0x0001这个地址,B指向0x0002这个地址
C = []      	# 对象C指向一个空列表
A.append(x)		# 对象A指向的空列表(地址:0x0001)中添加了'2'
B = B + x		# 开辟了一块儿新内存,其中值为'2',B不再指向''(地址:0x0002)而指向'2'
C.append(x)		# 对象C指向的空列表中添加了'2'
print(f"A:{A} B:{B} C:{C}") # A:['1', '2']  B:'2'  C:['2']

函数执行完后内存指向:

地址:0x0001   值:['1', '2'] ==> A
地址:0x0002   值:''
地址:0x0003   值:['2'] ==> C
地址:0x0004   值:'2'   ==> B

当第三次调用函数时

# 调用开始时: A指向0x0001这个地址,B指向0x0002这个地址
C = []      	# 对象C指向一个空列表
A.append(x)		# 对象A指向的空列表(地址:0x0001)中添加了'3'
B = B + x		# 开辟了一块儿新内存,其中值为'3',B不再指向''(地址:0x0002)而指向'3'
C.append(x)		# 对象C指向的空列表中添加了'3'
print(f"A:{A} B:{B} C:{C}") # A:['1', '2', '3']  B:'3'  C:['3']

函数执行完后内存指向:

地址:0x0001   值:['1', '2', '3'] ==> A
地址:0x0002   值:''
地址:0x0003   值:['3'] ==> C
地址:0x0004   值:'3'   ==> B

经过俺的这番讲解,朋友们你们学废了吗?

为了避免以后在程序中出现莫名其妙的 Bug,大家要记住在 Python 程序中

默认参数的值应该为不可变对象!!!

默认参数的值应该为不可变对象!!!

默认参数的值应该为不可变对象!!!

猜你喜欢

转载自blog.csdn.net/qq_43580193/article/details/107702997