V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
simpleapples
V2EX  ›  Python

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

  •  
  •   simpleapples · 2018-04-22 17:43:12 +08:00 · 3459 次点击
    这是一个创建于 2167 天前的主题,其中的信息可能已经有所发展或是发生改变。
    很多文章总结 python 传参,可变对象传引用不可变对象传值,这个说法不够准确。
    文章在这里: https://juejin.im/post/5adb33b16fb9a07acb3c71a6
    15 条回复    2018-04-23 17:41:43 +08:00
    Kilerd
        1
    Kilerd  
       2018-04-22 18:57:15 +08:00
    内容太简单。
    你指出的数字例外,不过只是 Python 对小整数的内存优化而已。

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

    像一楼说的,上面这个说法应该是不对的,并不是生成一个新对象,我猜是和 Java 类似的对小型整数有一个优化,提前定义了 128 以下的小整数
    Mistwave
        3
    Mistwave  
       2018-04-22 20:32:22 +08:00 via iPhone
    fluent python 有很好的讲解
    di94sh
        4
    di94sh  
       2018-04-22 20:41:25 +08:00 via Android
    额,python 会对小于 256 的整数做池化,你也可以吧任何你想池化的东西池化。
    lihongjie0209
        5
    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
        6
    simpleapples  
    OP
       2018-04-22 23:56:03 +08:00
    @Kilerd @kiwi95 python 确实是会把-5 到 256 的整形缓存 并且对大数也是有缓存的 我在这段里讲的是 python 对于不可变类型的处理 所以隐去了缓存的问题
    simpleapples
        7
    simpleapples  
    OP
       2018-04-22 23:56:39 +08:00
    @Mistwave effective python 也有很好的讲解
    simpleapples
        8
    simpleapples  
    OP
       2018-04-22 23:59:23 +08:00
    @lihongjie0209 感谢指正 更好的办法确实是 copy 一份
    Kilerd
        9
    Kilerd  
       2018-04-23 13:00:41 +08:00
    然而实际上 in python, everything is an object. 所以都是传引用。
    Kilerd
        10
    Kilerd  
       2018-04-23 13:02:17 +08:00
    如果用 rust 的思想来讲的话,某些内容满足了 Copy Trait, 所以内部修改不影响外部,最直观的表现就是传参前后的变量没有直接关系。
    jmc891205
        11
    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
        12
    simpleapples  
    OP
       2018-04-23 16:18:33 +08:00
    @jmc891205 我理解你第一句话的意思 但是不明白下面那句哪里不太准确 可不可以再详细说一下?
    jmc891205
        13
    jmc891205  
       2018-04-23 16:42:42 +08:00
    @simpleapples 你看我给的例子 在函数里修改传进来的可变对象参数 没对外部对象产生影响
    simpleapples
        14
    simpleapples  
    OP
       2018-04-23 17:23:53 +08:00
    @jmc891205 嗯 看到了 不过这个不是修改了 而是重新给 a 赋值 修改应该是 a+=[1]或者 a.append(1)
    这块字面意思上看确实有点不太准确 谢谢指正
    jmc891205
        15
    jmc891205  
       2018-04-23 17:41:43 +08:00
    @simpleapples 所以就回到我第一个回复的第一句话了,对于不可变对象,自增操作符实际上是一个赋值的操作。
    因此在讨论 Python 的传参的时候 不应该按可变对象不可变对象来分类 而应该而操作符 /函数调用的类型来分类。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2935 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 33ms · UTC 15:15 · PVG 23:15 · LAX 08:15 · JFK 11:15
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.