Python 的函数是怎么传递参数的?

a = [1,2,3]
def foo(b):
    b.append(4)
foo(a)
print(a)  #  [1,2,3,4]

def bar(c):
    c = [0,0,0]
bar(a)
print(a)  # [1,2,3,4]

既然说道传值还是传引用,就要说到c++了(据我所知python中没有这些概念)。假定题主和读者们对C++有所了解。首先复习一下实参和形参的概念,看foo函数,假设这是个c++函数,那么foo(a)调用过程中,a就是实参,b就是形参,如果对这个概念模糊,好好看下c++primer中的关于函数参数传递那一部分的内容,此处不再赘述。再来看看传值和传引用:

  • 传值是要把实参的值copy一份给形参作为值的,然后形参在函数中的操作就和实参没有关系了,这显然不是python中的传参方式,看 foo 函数就好理解了,若传值,b会copy一份[1,2,3],执行完b.append(4),变为[1,2,3,4],但是b的改变不会影响a,而事实上 a也改变了。
  • 传引用是使用形参给实参取一个别名(alias),函数中对形参的操作实际上就是对实参的操作,来举个c++中传引用的例子:
//命名空间和头文件此处省略了
void test(vector<int>& vi)
{
       vi = vector<int>{ 0,0,0 };
}
int main()
{
       vector<int> v{ 1,1,1 };
       test(v); // 因为是传引用,执行结束后 v 变成了vector<int>{ 0,0,0 }
       return 0;
}

可以看到python也不是传引用,把 bar 函数与此处的 test 函数作对比就知道了, 注意执行bar(a)以后,a不是[0,0,0], a没有改变,所以有答案说对于可变对象是传引用的说法我认为是不对的。

所以,既不是传值也不是传引用!要搞清楚python函数如何传参数这个问题,本质上要搞清楚的是python中的"name binding",我尽量用自己的话解释简洁一点,更多细节参看最后的链接。我把a, b, foo, bar 这些东西叫做name,而不是叫变量,因为如果使用一个未定义的东西xx,python会报 NameError: name 'xx' is not defined。python中name是没有类型的,而name所指向的对象是有类型的,比如name a 可以指向对象int数1,你也可以让它指向一个list对象。来看下面的代码:

x = 1
y = x
print(y is x) #True 注意此处的is用来比较 y 与 x 所指向的对象是否为同一个,也就是id(x)与id(y)是不是一样的
# id方法返回的是某对象的id号(一个int值),在其生命周期内保证唯一和不变, id(x)就是返回x所指向的对象的id
y = 2
print(y is x) # False
x += 1 # 此时x的值为2,  x绑定到了对象 int2 上,同时id(x)也会发生改变 
print(y is x) # True 

x = 1表示的是给对象int 1绑定了(binging)一个名字(name)叫做x,可以用名字 x来引用int 1 这个对象了。而 y=x 表示的是,现在y 也是对象int 1 的一个名字了,也可以用名字 y来引用对象int 1 了。而y和x是同一个对象的name,所以y is x返回True。现在 y=2, y is x 就是False了,因为name x 和y 指向了不同的对象。 x+=1, 此时注意 id(x)会发生改变, 返回的id是对象int 2的id了。那么你再执行 y is x,就返回True, 因为它们都指向对象int 2。

猜你喜欢

转载自blog.csdn.net/weixin_51267929/article/details/114142342