Python 代码对象、函数和默认参数
Python code object, function, and default parameters
调查
我有这个程序
def gen_baz():
return 2
def gen_bar(a, b, c=1):
return a, b, c
# ---------- #
# Save the code objects
c1 = gen_bar.__code__
c2 = gen_baz.__code__
dis.dis(gen_bar) # [Disassambly 1]
print(inspect.getsource(gen_bar)) # [Inspect 1]
bar = gen_bar(1, 2) # bar: (1, 2, 1)
baz = gen_baz() # baz: 2
print(f"before: bar={bar}, baz={baz}")
# --------- #
gen_bar.__code__, gen_baz.__code__ = c2, c1 # swap code objects between 2 functions
dis.dis(gen_baz) # [Disassambly 2]
print(inspect.getsource(gen_baz)) # [Inspect 1]
bar = gen_bar() # bar: 2
baz = gen_baz(1, 2) # baz: [Error 1]
print(f"after: bar={bar}, baz={baz}")
这里是反汇编和检查的输出
[Disassembly 1 & 2]
17 0 LOAD_FAST 0 (a)
2 LOAD_FAST 1 (b)
4 LOAD_FAST 2 (c)
6 BUILD_TUPLE 3
8 RETURN_VALUE
[Inspect 1 & 2]
def gen_bar(a, b, c=1):
return a, b, c
[Error 1]
Traceback (most recent call last):
File "C:\Users\something\test.py", line 32, in <module>
baz = gen_baz(1, 2)
TypeError: gen_baz() missing 1 required positional argument: 'c'
问题
- 为什么我在baz和bar之间交换代码对象后,程序崩溃了?即使它们具有相同的字节码 and inspect result
- python中的默认参数存放在哪里?它是否与函数对象一起出现而不是与代码对象一起出现?如果是这样,那么 Inspect 模块如何在第二次检查时给我
c=1
?
非常感谢!
勾选 docs:
__code__
: 包含已编译函数字节码的代码对象
__defaults__
:位置参数或关键字参数的任何默认值的元组
__kwdefaults__
:keyword-only 参数的任何默认值映射
默认值未存储在 code
对象中。它们直接存储在 function
对象中。
>>> gen_bar.__defaults__
(1,)
getsource
返回的文本,正如名称所暗示的那样,来自源代码本身,而不是由源代码生成的对象。 (在对象级别,签名仅由函数尝试从堆栈、全局命名空间等加载的值暗示)
您可以使用 inspect.signature
来查看 gen_baz
现在接受三个参数,但是 不 包括未传输到的默认值gen_baz
以及 code
对象。
>>> inspect.signature(gen_baz)
<Signature (a, b, c)>
默认参数值的使用隐藏在CALL_FUNCTION
操作码的计算中。鉴于
def foo(a=1):
return a
可以看到它的字节码都没有
>>> dis.dis(foo)
2 0 LOAD_FAST 0 (a)
2 RETURN_VALUE
无论有无显式参数都不会调用
>>> dis.dis('foo(9)')
1 0 LOAD_NAME 0 (foo)
2 LOAD_CONST 0 (9)
4 CALL_FUNCTION 1
6 RETURN_VALUE
>>> dis.dis('foo()')
1 0 LOAD_NAME 0 (foo)
2 CALL_FUNCTION 0
4 RETURN_VALUE
利用函数的 __defaults__
属性。在函数中,只是 假定 某些值可以从局部变量 a
推入堆栈,而不管该变量是如何设置的。在这两个调用中,在使用 CALL_FUNCTION
.
之前,一个值被简单地加载到堆栈上或不加载到堆栈上
调查
我有这个程序
def gen_baz():
return 2
def gen_bar(a, b, c=1):
return a, b, c
# ---------- #
# Save the code objects
c1 = gen_bar.__code__
c2 = gen_baz.__code__
dis.dis(gen_bar) # [Disassambly 1]
print(inspect.getsource(gen_bar)) # [Inspect 1]
bar = gen_bar(1, 2) # bar: (1, 2, 1)
baz = gen_baz() # baz: 2
print(f"before: bar={bar}, baz={baz}")
# --------- #
gen_bar.__code__, gen_baz.__code__ = c2, c1 # swap code objects between 2 functions
dis.dis(gen_baz) # [Disassambly 2]
print(inspect.getsource(gen_baz)) # [Inspect 1]
bar = gen_bar() # bar: 2
baz = gen_baz(1, 2) # baz: [Error 1]
print(f"after: bar={bar}, baz={baz}")
这里是反汇编和检查的输出
[Disassembly 1 & 2]
17 0 LOAD_FAST 0 (a)
2 LOAD_FAST 1 (b)
4 LOAD_FAST 2 (c)
6 BUILD_TUPLE 3
8 RETURN_VALUE
[Inspect 1 & 2]
def gen_bar(a, b, c=1):
return a, b, c
[Error 1]
Traceback (most recent call last):
File "C:\Users\something\test.py", line 32, in <module>
baz = gen_baz(1, 2)
TypeError: gen_baz() missing 1 required positional argument: 'c'
问题
- 为什么我在baz和bar之间交换代码对象后,程序崩溃了?即使它们具有相同的字节码 and inspect result
- python中的默认参数存放在哪里?它是否与函数对象一起出现而不是与代码对象一起出现?如果是这样,那么 Inspect 模块如何在第二次检查时给我
c=1
?
非常感谢!
勾选 docs:
__code__
: 包含已编译函数字节码的代码对象__defaults__
:位置参数或关键字参数的任何默认值的元组__kwdefaults__
:keyword-only 参数的任何默认值映射
默认值未存储在 code
对象中。它们直接存储在 function
对象中。
>>> gen_bar.__defaults__
(1,)
getsource
返回的文本,正如名称所暗示的那样,来自源代码本身,而不是由源代码生成的对象。 (在对象级别,签名仅由函数尝试从堆栈、全局命名空间等加载的值暗示)
您可以使用 inspect.signature
来查看 gen_baz
现在接受三个参数,但是 不 包括未传输到的默认值gen_baz
以及 code
对象。
>>> inspect.signature(gen_baz)
<Signature (a, b, c)>
默认参数值的使用隐藏在CALL_FUNCTION
操作码的计算中。鉴于
def foo(a=1):
return a
可以看到它的字节码都没有
>>> dis.dis(foo)
2 0 LOAD_FAST 0 (a)
2 RETURN_VALUE
无论有无显式参数都不会调用
>>> dis.dis('foo(9)')
1 0 LOAD_NAME 0 (foo)
2 LOAD_CONST 0 (9)
4 CALL_FUNCTION 1
6 RETURN_VALUE
>>> dis.dis('foo()')
1 0 LOAD_NAME 0 (foo)
2 CALL_FUNCTION 0
4 RETURN_VALUE
利用函数的 __defaults__
属性。在函数中,只是 假定 某些值可以从局部变量 a
推入堆栈,而不管该变量是如何设置的。在这两个调用中,在使用 CALL_FUNCTION
.