关于Python中函数传递参数为可变对象问题

关于Python中函数传递参数为可变对象问题

python函数传值机制:
python中,函数传递参数实际上就是给变量产生一个复制品,然后将复制品传入到函数中,而变量本身不收影响。

"""
 实现a、b变量的值交换,验证参数变化
"""
def data(a, b):
    a, b = b, a
    print("函数里a的值是:%d" % a)
    print("函数里b的值是:%d" % b)


a = 2
b = 3
data(a, b)
print("交换结束后,变量a的值是%d" % a)
print("交换结束后,变量a的值是%d" % b)

运行结果:

函数里a的值是:3
函数里b的值是:2
交换结束后,变量a的值是2
交换结束后,变量a的值是3

从上边结果可以看出,函数内部的参数a,b交换了值,但是参数本身的值并没有改变,
当在主程序中调用 data() 函数时,系统分别为主程序和 data() 函数分配两块内存,用于保存它们的局部变量。将主程序中的 a、b 变量作为参数值传入 data() 函数,实际上是在 data() 函数内存中重新产生了两个变量 a、b,并将主程序内存中 a、b 变量的值分别赋值给 data() 函数内存中的 a、b 参数(就是对 data() 函数的 a、b 两个变量进行初始化)。此时,系统存在两个 a 变量、两个 b 变量,只是存在于不同的内存地址中而己。
这就是值传递的实质:当系统开始执行函数时,系统对形参执行初始化,就是把实参变量的值赋给函数的形参变量,在函数中操作的并不是实际的实参变量。

Python函数参数的引用传递:
如果实际参数的数据类型是可变对象(列表、字典),则函数参数的传递方式将采用引用传递方式。需要注意的是,引用传递方式的底层实现,采用的依然还是值传递的方式。
代码验证函数传可变对象:

"""
 实现a、b变量的值交换,验证参数变化
"""


def data(d):
    d[0], d[1] = d[1], d[0]
    print("函数里d[0]的值是:%d" % d[0])
    print("函数里d[1]的值是:%d" % d[1])


d=[1,2]
data(d)
print("交换结束后,变量d[0]的值是%d" % d[0])
print("交换结束后,变量d[1]的值是%d" % d[1])

返回结果:

函数里d[0]的值是:2
函数里d[1]的值是:1
交换结束后,变量d[0]的值是2
交换结束后,变量d[1]的值是1

从上面的运行结果来看,在 data() 函数里,列表两个元素的值被交换成功。不仅如此,主程序中列表两个元素的值也被交换了。这很容易造成一种错觉,即在调用函数时,传入函数的就是 d列表本身,而不是它的复制品。但这只是一种错觉,下面还是结合示意图来说明程序的执行过程。

程序开始创建了一个列表对象,并定义了一个 d引用变量(其实就是一个指针)指向列表对象,这意味着此时内存中有两个东西:对象本身和指向该对象的引用变量。此时在系统内存中的存储示意图如图所示:

在这里插入图片描述
主程序创建了列表对象后存储示意图
接下来主程序开始调用 data() 函数,在调用函数时,d 变量作为参数传入data() 函数,这里依然采用值传递方式:把主程序中 d 变量的值赋给函数的 d形参,从而完成函数的 d参数的初始化。其实,主程序中的 d是一个引用变量(也就是一个指针),它保存了列表对象的地址值,当把 d的值赋给函数的 d参数后,就是让函数的 d参数也保存这个地址值,即也会引用到同一个列表对象。显示了 d列表传入函数后的存储示意图。
在这里插入图片描述
从图来看,这种参数传递方式是不折不扣的值传递方式,系统一样复制了d 的副本传入函数。但由于 d 只是一个引用变量,因此系统复制的是 d 变量,并未复制列表本身。
当程序在函数中操作 d 参数时,由于 d只是一个引用变量,故实际操作的还是列表对象。此时,不管是操作主程序中的 d变量,还是操作函数里的 d参数,其实操作的都是它们共同引用的列表对象,它们引用的是同一个列表对象。因此,当在函数中交换 d 参数所引用列表对象的 a、b 两个元素的值后,可以看到在主程序中 d 变量所引用列表对象的 a、b 两个元素的值也被交换了。
为了更好地证明主程序中的 d 和函数中的 d 是两个变量,在函数的最后一行增加如下代码:
d = None
运行上面代码,结果是函数中的 d 变量不再指向任何对象,程序其他地方没有任何改变。主程序调用函数后,再次访问 d 变量的 a、b 两个元素,依然可以输出 2、1。可见,主程序中的 d 变量没有受到任何影响。实际上,当在函数中增加“d =None”代码后,在内存中的存储示意图如图所示。
在这里插入图片描述
图 将 data() 函数中的 d 赋值为 None 后存储示意图

从图来看,把函数中的 d 赋值为 None 后,在函数中失去了对列表对象的引用,不可再访问该列表对象。但主程序中的 d 变量不受任何影响,依然可以引用该列表对象,所以依然可以输出列表对象的 a、b 元素的值。
通过上面介绍可以得出如下两个结论:

  1. 不管什么类型的参数,在 Python 函数中对参数直接使用“=”符号赋值是没用的,直接使用“=”符号赋值并不能改变参数。
  2. 如果需要让函数修改某些数据,则可以通过把这些数据包装成字典、列表等可变对象,然后把字典、列表等可变对象作为参数传入函数,在函数中通过字典、列表的方法修改它们,这样才能改变这些数据。
    我们在给函数传递可变对象时可能会遇到这个问题:
def linux_monitor(pid=0,pidlist = []):
    pidlist.append(pid)
    print(pid)
    print(pidlist)
 
linux_monitor()
linux_monitor()

结果如下:
在这里插入图片描述
第二次调用的时候,pidlist并不是空的。
所以对于可变参数,特别需要注意,如列表等都可能产生这样的问题。
我们可以对代码做如下处理即可正常使用:

def linux_monitor(pid=0,pidlist = None):
  if pidlist == None:
    pidlist = []
  pidlist.append(pid)
print(pid)  
print(pidlist)
  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值