Python函数传参机制深度解析:传值还是传引用?
本文深入解析Python函数参数传递机制,澄清传值与传引用的概念。通过代码示例说明Python采用“传对象引用”模式:对不可变对象(如整数)的操作类似传值,不影响原变量;对可变对象(如列表)的操作类似传引用,可修改原对象。理解这一机制有助于避免常见的变量引用错误。
首先,我们需要明确函数参数传递的基本概念,即区分“传值”与“传引用”的含义。
函数参数传递机制的核心在于调用函数与被调用函数在交互时如何进行数据通信。这一过程主要包含两种基本机制:值传递和引用传递。
在值传递过程中,被调函数的形式参数被视为局部变量。系统在堆栈中开辟新的内存空间,用于存储主调函数传入的实参值,这相当于创建了一个实参的副本。因此,值传递的特点是:被调函数对形式参数的任何修改仅作用于局部变量,不会改变主调函数中实参的值。
在引用传递过程中,被调函数的形式参数同样作为局部变量在堆栈中占用内存空间,但该空间存储的是主调函数中实参变量的内存地址。被调函数对形参的操作实际上是通过间接寻址,即通过堆栈中存储的地址去访问主调函数中的实参变量。因此,被调函数对形参的任何修改都会直接反映在主调函数的实参变量上。
那么,在Python语言中,实际的参数传递机制是如何运作的呢?
我们先通过一个基础的代码示例来观察:
from ctypes import *
import os.path
import sys
def test(c):
print "test before "
print id(c)
c+=2
print "test after +"
print id(c)
return c
def printIt(t):
for i in range(len(t)):
print t[i]
if __name__=="__main__":
a=2
print "main before invoke test"
print id(a)
n=test(a)
print "main afterf invoke test"
print a
print id(a)
运行上述代码后,输出结果如下:
>>> main before invoke test test before test after + main afterf invoke test 39601564
通过d函数可以获取对象的内存地址。从上述示例可以看出,变量a作为参数传递给test函数时,传递的是a的引用(即内存地址)。因此,在函数内部获取的变量C的地址与变量a的地址相同。然而,在函数内部对C执行赋值运算(C+=2)时,C的值从2变为4。此时,数值2和4所对应的内存空间均存在,但赋值操作后,C指向了值为4的新内存地址。而变量a仍然指向值为2的原始内存地址,因此后续打印a时,其值仍为2。
如果上述解释仍难以理解,可以参考以下补充示例:
>>> a=1 >>> b=1 >>> id(a) >>> id(b) >>> a=2 >>> id(a)

基于前文的例子,我们可以这样总结:

这是否意味着Python函数传参就是传引用?既然传参值在被调函数内被修改(如C的变化)却不影响主调函数的实参(a保持不变),这似乎与传引用的定义矛盾?让我们再看一个例子来验证。
from ctypes import *
import os.path
import sys
def test(list2):
print "test before "
print id(list2)
list2[1]=30
print "test after +"
print id(list2)
return list2
def printIt(t):
for i in range(len(t)):
print t[i]
if __name__=="__main__":
list1=["loleina",25,'female']
print "main before invoke test"
print id(list1)
list3=test(list1)
print "main afterf invoke test"
print list1
print id(list1)
实际运行结果如下:
>>> main before invoke test test before test after + main afterf invoke test ['loleina', 30, 'female']
我们发现,尽管传递方式看似相同,但第二个变量(列表)却发生了变化。这是为什么呢?
原因在于Python中的序列类型:列表是一个可变对象。基于list1=[1,2],当我们执行list1[0]=[0]时,查看list1的内存地址,发现其前后一致。
>>> list1=[1,2] >>> id(list1) >>> list1[0]=[0] >>> list1 [[0], 2] >>> id(list1)
结论:Python不允许程序员手动选择采用传值还是传引用,其参数传递机制采用的是“传对象引用”方式。这种方式实际上是传值和传引用的综合体现:如果函数接收到的是可变对象(如字典或列表)的引用,就可以修改该对象的原始内容,这相当于通过“传引用”来传递对象;如果函数接收到的是不可变对象(如数字、字符串或元组)的引用,则无法直接修改原始对象,这相当于通过“传值”来传递对象。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









