写了一篇关于 Python 参数传递的文章,请各位老铁指教 - V2EX
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 3989 次点击
    这是一个创建于 2729 天前的主题,其中的信息可能已经有所发展或是发生改变。
    很多文章总结 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     Solana     1039 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 18:24 PVG 02:24 LAX 11:24 JFK 14:24
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86