介绍
今天字节的面试官问到了这个问题,问:python函数对传入的变量进行一定的修改后,会影响函数外面这个变量的值吗,我回答的是如果是传入numpy数组就会改变。这个回答面试官估计是不满意的,现在就来认真研究一下这个问题。
有一些博客已经给出了很好的结论(python的参数传递是值传递还是引用传递?都不是!):
python不允许程序员选择采用传值还是传引用。python参数传递采用的肯定是“传对象引用”的方式。这种方式相当于传值和传引用的一种综合。如果函数收到的是一个不可变对象(数字、字符或元组)的引用,就不能直接修改原始对象–相当于通过‘值传递’来传递对象。如果函数收到的是一个可变对象(字典、列表)的引用,就能修改对象的原始值–相当于‘传引用’来传递对象。
那现在我们按照这个结论来实战一下,在这个过程中我也发现了一些新的东西
我的Python版本是:3.8.8
不可变对象
数字(Number)
def func(a, b):
a += a
b += 100
print('a={}, b={}'.format(a, b))
a = 1
b = 2
print('a={}, b={}'.format(a, b))
func(a, b)
print('a={}, b={}'.format(a, b))
打印结果:
a=1, b=2
a=2, b=102
a=1, b=2
可见在函数里面修改数字变量,不会影响外面的变量
字符串(String)
def func(a, b):
a = '_'.join([a, a])
b += '100'
print('a={}, b={}'.format(a, b))
a = '1'
b = '2'
print('a={}, b={}'.format(a, b))
func(a, b)
print('a={}, b={}'.format(a, b))
打印结果:
a=1, b=2
a=1_1, b=2100
a=1, b=2
可见在函数里面修改字符串变量,不会影响外面的变量
元组(Tuple)
def func(a, b):
# a[0] = 100 # 会报错
b += (100, 100)
print('a={}, b={}'.format(a, b))
a = (1, 1)
b = (2, 2)
print('a={}, b={}'.format(a, b))
func(a, b)
print('a={}, b={}'.format(a, b))
打印结果:
a=(1, 1), b=(2, 2)
a=(1, 1), b=(2, 2, 100, 100)
a=(1, 1), b=(2, 2)
可见在函数里面修改元组变量,不会影响外面的变量
可变对象
列表(List)
def func(a, b, c):
a[0] = 100 # 修改值
b.append(100) # 添加新的值
c = [100, 100] # 重新赋值
print('a={}, b={}, c={}'.format(a, b, c))
a = [1, 1]
b = [2, 2]
c = [3, 3]
print('a={}, b={}, c={}'.format(a, b, c))
func(a, b, c)
print('a={}, b={}, c={}'.format(a, b, c))
打印结果:
a=[1, 1], b=[2, 2], c=[3, 3]
a=[100, 1], b=[2, 2, 100], c=[100, 100]
a=[100, 1], b=[2, 2, 100], c=[3, 3]
可见对于列表变量,如果是修改值或者添加新的元素都会影响外面的变量,但如果是重新赋值就不会!!!
字典(Dict)
def func(a, b, c):
a[1] = 100 # 修改键值对
b[1] = 100 # 添加新的键值对
c = {
100: 100} # 重新赋值
print('a={}, b={}, c={}'.format(a, b, c))
a = {
1: 1}
b = {
2: 2}
c = {
3: 3}
print('a={}, b={}, c={}'.format(a, b, c))
func(a, b, c)
print('a={}, b={}, c={}'.format(a, b, c))
打印结果:
a={
1: 1}, b={
2: 2}, c={
3: 3}
a={
1: 100}, b={
2: 2, 1: 100}, c={
100: 100}
a={
1: 100}, b={
2: 2, 1: 100}, c={
3: 3}
同列表,如果是修改值或者添加新的元素都会影响外面的变量,但如果是重新赋值就不会!!!
numpy数组
import numpy as np
def func(a, b, c):
a[0] = 100 # 修改值
b = np.concatenate((b, b), 0) # 添加新的值
c = np.array([100, 100]) # 重新赋值
print('a={}, b={}, c={}'.format(a, b, c))
a = np.array([1, 1])
b = np.array([2, 2])
c = np.array([3, 3])
print('a={}, b={}, c={}'.format(a, b, c))
func(a, b, c)
print('a={}, b={}, c={}'.format(a, b, c))
打印结果:
a=[1 1], b=[2 2], c=[3 3]
a=[100 1], b=[2 2 2 2], c=[100 100]
a=[100 1], b=[2 2], c=[3 3]
同列表,如果是修改值会影响外面的变量,但如果是重新赋值(concatenate也重新赋值了)就不会!!!
结论
- 当函数传入的参数是不可变对象时(数字,字符串,元组),在函数里面做的任何操作都不会对外面的变量造成影响
- 当函数传入的参数是可变对象时(列表,字典,以及numpy数组),这个时候传入的参数其实是变量的地址指针,在这上面对变量进行修改也自然影响了外面的变量。例如在函数里面对变量进行修改元素以及添加新元素时(没有开辟新空间),就会影响外面的变量。但如果是重新赋值,就不会影响,这是因为重复赋值已经为变量开辟了新的空间,而不是去修改之间的变量。