Python参数传递机制详解,请各位老铁指教
很多文章总结 python 传参,可变对象传引用不可变对象传值,这个说法不够准确。
文章在这里: https://juejin.im/post/5adb33b16fb9a07acb3c71a6
Python参数传递机制详解,请各位老铁指教
内容太简单。
你指出的数字例外,不过只是 Python 对小整数的内存优化而已。
PS:大神不要喷我。
“ Python 会将这个变量指向的对象加 2 后,生成一个新的对象,然后再让 i 指向这个新的对象”
像一楼说的,上面这个说法应该是不对的,并不是生成一个新对象,我猜是和 Java 类似的对小型整数有一个优化,提前定义了 128 以下的小整数
fluent python 有很好的讲解
额,python 会对小于 256 的整数做池化,你也可以吧任何你想池化的东西池化。
解决这个问题,推荐再参数传递可变对象时,默认值设置为 None,在函数内部对 None 进行判断后再赋予默认值。
def test(b=None):
b = b or []
b += [1]
print(b)
test() # [1]
test() # [1]
test() # [1]
这个做法也没有问题, 但是更好的办法是:
不要修改参数!!!
用的时候复制一份
def test(b=[]):
var = list(b)
var += [1]
print(var)
####################### 上面的是理论部分################################
####################### 实践部分 #####################################
def test(b=None):
b = b or []
b += [1]
print(b)
这种函数真的不会出现在实际代码中, 因为这个函数一点用的没有
函数大概分三种:
1. 无副作用的查询函数: getXXX(), queryXXX()
这种函数有个特点 : 他们都有返回值.
2. 有副作用的修改函数: list.sort()
这种函数有个特点: 他们会改变调用者 /参数的状态, 但是没有返回这
3. 混血: 既有返回值又有副作用:
比如你有一个函数: getUser(id), 会返回一个 User, 但是在调用的时候它把 User 的 queryCount 属性改变了
def test(b=None):
b = b or []
b += [1]
print(b)
这个函数首先没有返回值, 其次所有的状态都发生在函数作用域之内, 你调用完之后所有的状态都被销毁, 所以也没有副作用.
所有在生产环境中如果看到这种函数,请删了吧, 真的除了增加代码量一点用都没有.
python 确实是会把-5 到 256 的整形缓存 并且对大数也是有缓存的 我在这段里讲的是 python 对于不可变类型的处理 所以隐去了缓存的问题
effective python 也有很好的讲解
感谢指正 更好的办法确实是 copy 一份
然而实际上 in python, everything is an object. 所以都是传引用。
如果用 rust 的思想来讲的话,某些内容满足了 Copy Trait, 所以内部修改不影响外部,最直观的表现就是传参前后的变量没有直接关系。
自增操作符对不可变对象其实是生成一个新的 object 然后修改引用到这个新的 object。
不可变对象和可变对象的自增操作符的行为是完全不同的。
所以我觉得下面这句话不太准确
> 修改传进的可变参数时,会对外部对象产生影响,修改不可变参数时则不会影响。
比如这段:
a = [1, 2, 3]
print(id(a)) # 1437494204232
def mutable(a):
----print(id(a)) # 1437494204232
----a = [2, 3, 4]
----print(id(a)) # 1437494207528
mutable(a)
print(a) # [1, 2, 3]
print(id(a)) # 1437494204232
我理解你第一句话的意思 但是不明白下面那句哪里不太准确 可不可以再详细说一下?
你看我给的例子 在函数里修改传进来的可变对象参数 没对外部对象产生影响
所以就回到我第一个回复的第一句话了,对于不可变对象,自增操作符实际上是一个赋值的操作。
因此在讨论 Python 的传参的时候 不应该按可变对象不可变对象来分类 而应该而操作符 /函数调用的类型来分类。


