写了一篇关于 Python 参数传递的文章,请各位老铁指教

2018-04-22 17:43:12 +08:00
 simpleapples
很多文章总结 python 传参,可变对象传引用不可变对象传值,这个说法不够准确。
文章在这里: https://juejin.im/post/5adb33b16fb9a07acb3c71a6
3665 次点击
所在节点    Python
15 条回复
Kilerd
2018-04-22 18:57:15 +08:00
内容太简单。
你指出的数字例外,不过只是 Python 对小整数的内存优化而已。

PS:大神不要喷我。
kiwi95
2018-04-22 20:21:14 +08:00
“ Python 会将这个变量指向的对象加 2 后,生成一个新的对象,然后再让 i 指向这个新的对象”

像一楼说的,上面这个说法应该是不对的,并不是生成一个新对象,我猜是和 Java 类似的对小型整数有一个优化,提前定义了 128 以下的小整数
Mistwave
2018-04-22 20:32:22 +08:00
fluent python 有很好的讲解
di94sh
2018-04-22 20:41:25 +08:00
额,python 会对小于 256 的整数做池化,你也可以吧任何你想池化的东西池化。
lihongjie0209
2018-04-22 21:02:55 +08:00
解决这个问题,推荐再参数传递可变对象时,默认值设置为 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)


这个函数首先没有返回值, 其次所有的状态都发生在函数作用域之内, 你调用完之后所有的状态都被销毁, 所以也没有副作用.

所有在生产环境中如果看到这种函数,请删了吧, 真的除了增加代码量一点用都没有.
simpleapples
2018-04-22 23:56:03 +08:00
@Kilerd @kiwi95 python 确实是会把-5 到 256 的整形缓存 并且对大数也是有缓存的 我在这段里讲的是 python 对于不可变类型的处理 所以隐去了缓存的问题
simpleapples
2018-04-22 23:56:39 +08:00
@Mistwave effective python 也有很好的讲解
simpleapples
2018-04-22 23:59:23 +08:00
@lihongjie0209 感谢指正 更好的办法确实是 copy 一份
Kilerd
2018-04-23 13:00:41 +08:00
然而实际上 in python, everything is an object. 所以都是传引用。
Kilerd
2018-04-23 13:02:17 +08:00
如果用 rust 的思想来讲的话,某些内容满足了 Copy Trait, 所以内部修改不影响外部,最直观的表现就是传参前后的变量没有直接关系。
jmc891205
2018-04-23 15:50:53 +08:00
自增操作符对不可变对象其实是生成一个新的 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
simpleapples
2018-04-23 16:18:33 +08:00
@jmc891205 我理解你第一句话的意思 但是不明白下面那句哪里不太准确 可不可以再详细说一下?
jmc891205
2018-04-23 16:42:42 +08:00
@simpleapples 你看我给的例子 在函数里修改传进来的可变对象参数 没对外部对象产生影响
simpleapples
2018-04-23 17:23:53 +08:00
@jmc891205 嗯 看到了 不过这个不是修改了 而是重新给 a 赋值 修改应该是 a+=[1]或者 a.append(1)
这块字面意思上看确实有点不太准确 谢谢指正
jmc891205
2018-04-23 17:41:43 +08:00
@simpleapples 所以就回到我第一个回复的第一句话了,对于不可变对象,自增操作符实际上是一个赋值的操作。
因此在讨论 Python 的传参的时候 不应该按可变对象不可变对象来分类 而应该而操作符 /函数调用的类型来分类。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/448911

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX