Instantiation time for mutable default arguments of closures in Python
我的理解是,当python解析函数的源代码时,它将其编译为字节码,但在调用函数之前不运行此字节码(这就是为什么函数中的非法变量名不会引发异常,除非调用该函数)。
在函数的初始设置过程中,不会实例化默认参数,但只有在第一次调用函数时才会实例化默认参数,无论是否提供了参数。默认参数的这个相同实例用于所有将来的调用,可以通过使用可变类型作为默认参数来查看。
但是,如果我们将函数放在另一个函数中,那么在每次调用外部函数时,默认参数现在似乎都会被重新实例化,如下代码所示:
1 2 3 4 5 6 7 8 9 10 11 | def f(x): def g(y, a=[]): a.append(y) return a for y in range(x, x + 2): print('calling g from f:', g(y)) return g(y + 1) for x in range(2): print('calling f from module scope:', f(x)) |
这打印出来了
1 2 3 4 5 6 | calling g from f: [0] calling g from f: [0, 1] calling f from module scope: [0, 1, 2] calling g from f: [1] calling g from f: [1, 2] calling f from module scope: [1, 2, 3] |
这是否意味着每次调用
内部函数是使用内部函数的现有字节码重建的。使用
1 2 3 4 5 6 7 8 9 10 11 12 | >>> import dis >>> def make_func(): ... def my_func(): ... pass ... return my_func >>> dis.dis(make_func.__code__) 3 0 LOAD_CONST 1 (<code object my_func at [...]", line 3>) 3 MAKE_FUNCTION 0 6 STORE_FAST 0 (my_func) 5 9 LOAD_FAST 0 (my_func) 12 RETURN_VALUE |
如果你这样做:
1 2 3 4 5 6 | >>> f1 = make_func() >>> f2 = make_func() >>> f1 is f2 False >>> f1.__code__ is f2.__code__ True |
第一个误解是:"当python解析函数的源代码时,它将其编译为字节码,但在调用函数之前不运行这个字节码(这就是为什么函数中的非法变量名不会引发异常,除非调用函数)。"要清楚,您的误解是"函数中的非法变量名除非调用函数,否则不会引发异常"。在执行函数之前,不会捕获未分配的名称。
看看这个简单的测试:
1 2 3 4 | In [1]: def g(a): ...: 123onetwothree = a File"<ipython-input-5-48a83ac30c7b>", line 2 123onetwothree = a |
第二个误解是:"在函数的初始设置过程中,不会实例化默认参数,但仅当函数第一次被调用时……"。这是不正确的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | In [7]: def f(x=[]): ...: print(x) ...: x.append(1) ...: print(x) ...: ...: In [8]: f.__defaults__ Out[8]: ([],) In [9]: f() [] [1] In [10]: f.__defaults__ Out[10]: ([1],) In [11]: |
例如,每次运行
回应评论
1 2 3 4 5 6 7 | In [11]: def f(x=h()): pass --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-11-ecbb4a9f8673> in <module>() ----> 1 def f(x=h()): pass NameError: name 'h' is not defined |
只需看看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | dis(f) 2 0 BUILD_LIST 0 3 LOAD_CONST 1 (<code object g at 0x7febd88411e0, file"<ipython-input-21-f2ef9ebb6765>", line 2>) 6 LOAD_CONST 2 ('f.<locals>.g') 9 MAKE_FUNCTION 1 12 STORE_FAST 1 (g) 6 15 SETUP_LOOP 46 (to 64) 18 LOAD_GLOBAL 0 (range) 21 LOAD_FAST 0 (x) 24 LOAD_FAST 0 (x) 27 LOAD_CONST 3 (2) 30 BINARY_ADD 31 CALL_FUNCTION 2 (2 positional, 0 keyword pair) 34 GET_ITER >> 35 FOR_ITER 25 (to 63) 38 STORE_FAST 2 (y) |
(剪短以保持简洁)
为
1 | 3 LOAD_CONST 1 (<code object g at 0x7febd88411e0, file"<ipython-input-21-f2ef9ebb6765>", line 2>) |
不包含任何可变结构,它只包含可执行代码和其他不可变信息。你也可以看一眼:
1 2 3 4 5 6 7 8 9 | dis(f.__code__.co_consts[1]) 3 0 LOAD_FAST 1 (a) 3 LOAD_ATTR 0 (append) 6 LOAD_FAST 0 (y) 9 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 12 POP_TOP 4 13 LOAD_FAST 1 (a) 16 RETURN_VALUE |
每次调用