![1e3be05ee9a6ba8765c5c5928cae2761.png](https://i-blog.csdnimg.cn/blog_migrate/504e5c70349f624c1b4f7cfb5ea676be.jpeg)
Python 到底是传值还是传引用?
这个问题我看到过很多解释,一种比较普遍的解释是这样的:
Python 中同时存在传值和传引用,如果参数是基本类型,那么就是传值调用,如果参数是容器类型,那么就是传引用调用。
如果有 C++ 编程经验就会发现,这可能并不是事实。
函数如何调用
首先,我们需要搞清楚函数调用中参数是如何传递的,这是函数传值或者传引用最根本的区别。
在《深入 Python》系列中我们已知,函数调用会在调用栈中创建新的栈帧(Frame),其中包含了函数中所需要的所有局部变量,Python 的参数也被包含在函数自身的 local 字典中:
def test(a, b):
c = 5
# 打印所有局部变量
print(locals())
return (a + b) * c
x = 1
y = 2
test(x, y)
# 输出
# {'a': 1, 'b': 2, 'c': 5}
所以在 Python 的调用中,发生了 x->a, y->b 的赋值操作,所以在 test 函数中的 a,b 是 x,y 的副本,只不过是他们的值相同,所以被称为传值调用。
如果是传引用调用,那么在函数中应当可以直接访问外部参数,而非栈帧中的副本。
反例
很多说 Python 函数调用也可能是传引用,一个例子是函数的参数是容器的时候,可以通过参数改变容器内容。
其实以此说明传入的容器是传引用调用是不合适的。如果函数是传引用调用,那么我们应当可以在函数内直接交换参数。
def swap(a, b):
tmp = a
a = b
b = tmp
x = [1,2,3]
y = ['a', 'b', 'c']
swap(x, y)
# 输出 x
# [1, 2, 3]
# 输出 y
# ['a', 'b', 'c']
# 没有变化
但实际上并不能。所以当参数是容器的时候,函数也是传值调用,只不过这个值正好是个容器的引用而已。
更严格的说,Python 中所有变量都是 “引用”,它的行为更接近 C 语言中的指针。
C++ 中的传值和传引用
C++ 同时支持两种传值方式,下面是一段 demo:
#include <iostream>
// 传入指针变量的 引用
void swap(int * &a, int * &b){
int * tmp = a;
a = b;
b = tmp;
}
// 传入指针变量
void bad_swap(int * a, int * b){
int * tmp = a;
a = b;
b = tmp;
}
int main(){
int a[] = {1,2,3};
int b[] = {5,4,3};
int * ap = a;
int * bp = b;
std::cout<< ap[0] << std::endl; // 输出 1
swap(ap, bp); // 传引用调用,函数直接改变了两个参数
std::cout<< ap[0] << std::endl; // 输出 5
bad_swap(ap, bp); // 传值调用,改变的只是函数栈中的局部变量,无效
std::cout<< ap[0] << std::endl; // 无变化,依然输出 5
return 1;
}
注意 swap(ap, bp); 和 bad_swap(ap, bp); 两个函数的调用参数完全一样,但只有传引用调用才可以影响实参。
而 Python 的函数调用相当于 bad_swap,确实是传值调用。
首发公众号 “江川Go”,关注了解程序员的烧脑日常。
![42f060c8b085e6e67fec5d77be062e0f.png](https://i-blog.csdnimg.cn/blog_migrate/36841992e556eea741406c655a0ab973.jpeg)