文章目录
python和golang对比系列(二)–> 参数传递方式
在编程语言深入讨论中,经常被大家提起也是争论最多的讨论之一就是按值(by value)还是按引用传递(by reference, by pointer)
一、golang中的参数传递
在golang中,严格意义上来说,只有一种传递方式,那就是按值传递,即在函数内部修改传入参数的值时函数外部传入值的拷贝,传入时发生了复制行为。无论是变量本身或者是将变量当做指针被传递,都是创建一个副本(值的副本或者是一个指向该变量的指针的副本),T类型的变量和*T类型的变量在当做函数或者方法的参数时会传递它的副本。
如果要想实现和使用按引用传递,则需要将传入的参数设置为指针类型;可以看起来达到按引用传递的效果,实际还是按值传递的。
传递方式 | 数据类型 |
---|---|
值传递 | int、float、bool、string、数组、结构体 |
引用传递 | 指针、切片、map、管道、接口 |
1.1 如何选择T 和 *T
在定义函数和方法的时候,作为一位资深的Go开发人员,一定会对函数的参数和返回值定义成T和T深思熟虑,有些情况下可能还会有些苦恼。
那么什么时候才应该把参数定义成类型T,什么情况下定义成类型T呢。
一般的判断标准是看副本创建的成本和需求。
- 不想变量被修改。 如果你不想变量被函数和方法所修改,那么选择类型T。相反,如果想修改原始的变量,则选择*T
- 如果变量是一个大的struct或者数组,则副本的创建相对会影响性能,这个时候考虑使用*T,只创建新的指针,这个区别是巨大的
- (不针对函数参数,只针对本地变量)对于函数作用域内的参数,如果定义成T,Go编译器尽量将对象分配到栈上,而*T很可能会分配到对象上,这对垃圾回收会有影响
1.2 发生副本创建的情形
- 对变量的赋值
- slice,map和数组在初始化和按索引设置时
- for-range循环
- 往channel中send对象时
- 函数参数和返回值
- 方法Receiver
1.3 不同类型的副本创建
- bool,数值和指针
对象很小,创建副本的开销可以忽略 - 数组
对于大的数组的参数传递和赋值,一定要慎重 - map、slice 和 channel
指向指针类型,指向一个底层的数据结构 - 字符串
大部分情况下你不需要定义成*string。唯一的例外你需要 nil值的时候。"“和nil表示的意义是不一样的,”"表示字段存在,只不过字符串是空值,而nil表示字段不存在。 - 函数
指针类型
二、python中的参数传递
python的函数调用到底是传值还是传引用?
2.1 python的变量内存模型
对于python而言,一切皆对象,python为每个对象分配内存空间,但是并非为每个变量分配内存空间,因为在python中,变量更像是一个标签,就像在现实生活中,一个人可以有多种身份标签,比如:XX的父亲,XX的儿子,XX的工程师,X地志愿者等等,但对应的实体都是同一个人,只占同一份资源。
2.2 python的间接引用机制
类型 | 对象 |
---|---|
可变类型 | 列表,字典 |
不可变类型 | int、float、字符串、元组 |
2.3 python传参时可变类型和不可变类型的区别
- 在参数传递时,实参将标签复制给了形参,这个时候形参和实参都是指向同一个对象。
- 在函数内修改形参:
2.1 对于不可变类型变量而言:因为不可变类型变量特性,修改变量需要新创建一个对象,形参的标签转而指向新对象,而实参没有变
2.2 对于可变类型变量而言,因为可变类型变量特性,直接在原对象上修改,因为此时形参和实参都是指向同一个对象,所以,实参指向的对象自然就被修改了。
这种机制存在于整个python环境中,而不仅仅是参数传递中。