有关Python参数的思考

小编 DevOps视角

引子

    在群里和人讨论了有关python参数传递的机制。对方说是赋值传递,我持的观点是引用传递,讨论了许久,对方仍没讲解明白赋值传递的实现。我查看官方文档,官方文档的描述为:

Remember that arguments are passed by assignment in Python.将官方文档切换为中文结果为:请记住在 Python 中参数是通过赋值来传递的。之前看官方文档也没去深究,经过今天的讨论,浅显地对探究了官方文档的这段话,进行了一些思考。
英文版文档链接:https://docs.python.org/3/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference
中文版文档链接:https://docs.python.org/zh-cn/3/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference

值传递与引用传递


    值传递的方式,传入函数的参数是实际参数的复制品,不管在函数中对这个复制品如何操作,实际参数本身不会受到任何影响     引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中所进行的修改,将影响实际参数。

python函数的参数的传递


    对于pythoner来说,在python中一切皆对象是众所周知的。对象又分为类似于数字不可变的对象和类似于字典可变的对象。    先看下面的代码
def test(a , b) :   a, b = b, a  print("test函数里,a的值是", a, ";b的值是", b)  print("test函数里,a的内存地址是", id(a), ";b的内存地址是", id(b))a = 1b = 2test(a , b)print("交换结束后,a的值是", a , ";变量b的值是", b)print("交换结束后,a的内存地址是", id(a) , ";b的内存地址是", id(b))
       运行的结果如下
test函数里,a的值是 2 ;b的值是 1test函数里,a的内存地址是 140705270535872 ;b的内存地址是 140705270535840交换结束后,a的值是 1 ;变量b的值是 2交换结束后,a的内存地址是 140705270535840 ;b的内存地址是 140705270535872
    从运行结果来看,在test函数里,a和b的值分别是2和1,交换结束后,变量a和b的值依然是1和2,并没有发生改变,程序中定义的变量a和b并不是test函数里的a和b。    可以推测,test函数里的a和b只是主程序中变量a和b的复制品。    代码开始只是定义了a和b两个变量,当程序运行到test函数时,进入 test函数,并将主程序中的a、b作为变量传入到test函数,此时,系统分别为主程序和test函数各分配一个栈区,用于保存各自的变量。   将主程序中的a、b作为变量传入到test函数,实际上是在test函数栈区重新生成了两个变量a、b,并将主程序中a、b变量的值赋值给test函数栈区中的a和b,即对test函数中的a和b进行初始化。可以理解,是将a=1和b=2传递给了test函数,因此可以理解为将赋值传递给了test函数。只是在test栈区内,将test栈区内将test栈区a、b两变量所对应的内存地址交换了,但并未影响到主栈区内的a和b。


再看下面的代码

def test(d):  d['a'], d['b'] = d['b'], d['a']  print("test函数里,a元素的值是",d['a'], ";b元素的值是", d['b'])  print("test函数里,a元素的内存地址是",id(['a']), ";b元素的值是", id(d['b']))d = {'a': 1, 'b': 2}test(d)print("交换结束后,a元素的值是",d['a'], ";b元素的值是", d['b'])print("交换结束后,a元素的内存地址是",id(['a']), ";b元素的值是", id(d['b']))
       运行结果如下
test函数里,a元素的值是 2 ;b元素的值是 1test函数里,a元素的内存地址是 1612700978880 ;b元素的值是 140705270535840交换结束后,a元素的值是 2 ;b元素的值是 1交换结束后,a元素的内存地址是 1612700978880 ;b元素的值是 140705270535840

和猜想的结果一样吗?
其实也是一样的,主栈区将d = {'a': 1, 'b': 2}传递给了test栈区,只是test栈区中用d['a'], d['b'] = d['b'], d['a']进行交换时,使用的是对{'a': 1, 'b': 2}这个字典本身进行的操作,并不是对test栈区中的d进行的操作。代码修改如下

def test(d):  d['a'], d['b'] = d['b'], d['a']  print("test函数里,a元素的值是",d['a'], ";b元素的值是", d['b'])  print("test函数里,a元素的内存地址是",id(['a']), ";b元素的值是", id(d['b']))  d = Noned = {'a': 1, 'b': 2}test(d)print("交换结束后,a元素的值是",d['a'], ";b元素的值是", d['b'])print("交换结束后,a元素的内存地址是",id(['a']), ";b元素的值是", id(d['b']))
      运行结果```

test函数里,a元素的值是 2 ;b元素的值是 1
test函数里,a元素的内存地址是 2532485631680 ;b元素的值是 140705270535840
交换结束后,a元素的值是 2 ;b元素的值是 1
交换结束后,a元素的内存地址是 2532485631680 ;b元素的值是 140705270535840

发现并没对{'a': 1, 'b': 2}本身进行修改。
官方文档接下一句是Since assignment just creates references to objects, there’s no alias between an argument name in the caller and callee, and so no call-by-reference per se.

总结

1、不能将python函数的参数传递分为可变对象和不可变对象来说明
2、python参数的传递不是值传递也不是引用传递,而是通过赋值来传递
3、直接使用“=”来赋值是没有效果的
4、如果要让函数修改数据,可以将这些数据封装成为可变对象
以上即为对python参数方面的思考,不足、思虑不到之处,请斧正。

思考题

最后给出一段代码:

def test(l):   l[0] = "a"   l = [4, 5, 6]l = [1, 2, 3]test(l)print(l)

运行结果是什么?
首先大家不要运行,先预测下运行结果,并把预测结果贴在留言区,然后再运行,看看是否和预测的结果一样。

©著作权归作者所有:来自51CTO博客作者mob604756e75222的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. php函数知识
  2. 线性表之链式存储(二)
  3. JQuery初学习之`attr()`、`css()`、`val()`、`html()`、`text()`
  4. 什么是技术分析(Technical Analysis)指标库
  5. C语言——C语言常用关键字总结(新手 还请不吝赐教)(一)
  6. c++ this
  7. python关于range函数总结
  8. 函数递归使用
  9. Midway Serverless 发布 2.0,一体化让前端研发再次提效

随机推荐

  1. android 用VideoView播放本地视频文件
  2. 【Android】简单图片浏览器
  3. Android 解决Toast的延时显示问题
  4. Android中编码实现软件界面
  5. Android APP--建立简单的交互界面
  6. Android应用中使用Popupmenu
  7. Android安卓开发官方文档国内镜像
  8. android欢迎界面并执行任务
  9. Android中添加syslog功能
  10. SMS Library in Android