Re: [问题] None在def中的变化

楼主: gmccntzx1 (o.O)   2020-04-04 16:28:44
这个**问题**其实被蛮多人讨论过了,但比起争论它是否是 Python 的设计瑕疵,我认为
深入了解其背后的运作原理是更值得做的事情。
这边我先以原 PO 所问的这个段内容起头:
> ... 想请问为什么 ex2 里引述默认值改为 None 时,不会发生印出的内容包含前一次
> 呼叫内容,第一次输出['a']后,result不是已经变成['a']了吗 ...
简单版的回答是:
在那个 function 内所用到的 `result` 一开始指向的是 `None`,如同 signature
里的 `result=None` 所表示。所以在执行 `if result is None:` 时,所得到的结
果是 True ,因此 `result` 就如下一行一样,被重新指向一个新的 empty list。
(延伸阅读: name binding, https://nedbatchelder.com/text/names.html )
详细版的回答:
一开始, `result` 指向的物件是存在 local scope 里的。而因为该物件是 `None`
,所以 `result` 也就是指向 `None`。而要知道一开始 local scope 内有什么东西
,你可以在 if 的上一行加上 `print(locals())` 来观察。
为求接下来撰文方便,我们用官方文件中的例子来说明,请参考下图:
https://i.imgur.com/yeMxEP9.png
程式执行到 `print(f(1))` 时,`print` 里的 function call `f(1)` 会先被执
行。而因为 `f` 第一个参数 `a` 所拿到的值是 1,而 `L` 没有被指定,所以进入
function 后,`locals()` 回传的内容会是 `{'a': 1, 'L': []}`。
在执行 `L.append(a)` 之后,`L` 这个 list 的内容变成了 `[1]`。但是,记得
前面提到的 name binding 吗?由于 `L` 指向的正是 local scope 的那个 `L`,
所以如果接着再呼叫一次 `locals()`,回传的内容会是 `{'a': 1, 'L': [1]}`。
因此执行到 `print(f(2))` 时,由于稍早在 `f` 的 local scope 内的 `L` 已经
被改变了,所以这时候 `print(locals())` 里看到的 `L` 就是已经被改变的状态。
(不过使用 `locals()` 来观察一个 function 被执行时其 local scope 的内容并
不完全适合,**详细的原因后续会再说明**。)
但是这跟 mutable/immutable object 有关系吗?以这个例子来说其实不太适合,
让我们将它稍微改写成以下两个版本:
- mutable default argument
https://i.imgur.com/ole5dma.png
- immutable default argument
https://i.imgur.com/f13zzlx.png
这样一来,就可以很明显地了解这个问题跟使用 mutable/immutable object 作为
默认值的差别了。
然而,我们知道了 `locals()` 的用处,那是否可以用它来做些有趣的事情呢?
譬如,直接使用 `locals()` 去修改默认值(暂不考虑有传入 `L` 的情况)?
https://i.imgur.com/Wozmmqy.png
很抱歉,失败了。原因有点复杂,恕我在此省略。但其实这点在官方文件也有提到
> Note: The contents of this dictionary should not be modified;
> changes may not affect the values of local and free variables used
> by the interpreter.
https://docs.python.org/3/library/functions.html#locals
但有没有其他办法去达到这个目的呢?其实还是有的,方法如下
https://i.imgur.com/PT83bOF.png
不过很明显地,比起这么做,倒不如用常见的方法:将默认值设为 `None`,然后在函
数内用 if 判断以重新设定。
稍微扯远了。这个问题的根本还是需要回到 "参数初始化的方式" 来讨论。原因也如同
官方文件所提到的
> ... The default value is evaluated only once ...
https://docs.python.org/3/tutorial/controlflow.html#more-on-defining-functions
还有,如果 `result` 从一开始就没有被定义在 function signature 里的话,在
local scope 内就不会 `result`。在这种情况下,便会循着所谓的 LEGB rule (
local, enclosed, global, built-in) 去做 name resolution 。
(延伸阅读: LEGB rule,
https://sebastianraschka.com/Articles/2014_python_scope_and_namespaces.html )
作者: s860134 (s860134)   2020-04-04 17:47:00
这是 markdown 吧?
作者: pmove (金疾柠檬)   2020-04-04 17:57:00
从底层的byte code讲,当然这是真正的因就像讲微分题目,从Lim 讲起,这当然可以。只是我有点吓到
作者: jiyu520 (不要鲫鱼我)   2020-04-04 18:10:00
学习了 谢谢
作者: shezion (= =)   2020-04-09 00:38:00
好专业的说明,感谢

Links booklink

Contact Us: admin [ a t ] ucptt.com