Python函数传参是值传递还是引用传递?

介绍

今天字节的面试官问到了这个问题,问: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也重新赋值了)就不会!!!

结论

  1. 当函数传入的参数是不可变对象时(数字,字符串,元组),在函数里面做的任何操作都不会对外面的变量造成影响
  2. 当函数传入的参数是可变对象时(列表,字典,以及numpy数组),这个时候传入的参数其实是变量的地址指针,在这上面对变量进行修改也自然影响了外面的变量。例如在函数里面对变量进行修改元素以及添加新元素时(没有开辟新空间),就会影响外面的变量。但如果是重新赋值,就不会影响,这是因为重复赋值已经为变量开辟了新的空间,而不是去修改之间的变量。

猜你喜欢

转载自blog.csdn.net/qq_33757398/article/details/125773117