返回函数或 lambda 函数有区别吗?

Is there a difference in returning a function or a lambda function?

提问人:david 提问时间:3/30/2023 更新时间:3/30/2023 访问量:60

问:

以下两个代码之间是否有区别(甚至是微妙的)? (第二代码有什么特殊用例吗?

  • 代码 1
def f(n):
    def g(x):
        return x * n
    return g
  • 代码 2
def f(n):
    def g(x):
        return x * n
    return lambda y: g(y)
Python 函数 闭包

评论

1赞 Michael Butscher 3/30/2023
该函数带有其(不一定是变量名称),而 lambda 则没有。回溯会略有不同,第二个变体的效率略低,因为有额外的函数调用。g__name__
0赞 Mous 3/30/2023
返回值的属性存在一些细微的差异,它定义了函数的属性,正如 michael 所提到的,它只与非 lambda 函数相关。__code____name__
3赞 juanpa.arrivillaga 3/30/2023
“第二代码有什么特殊用例吗?” 没有。这是一种令人费解的方式,只是说是否最终具有相同的签名。这是毫无意义的,在任何代码审查中,我都会让人们将其更改为ggg
0赞 chepner 3/30/2023
一个更好的问题是代码 3 () 是否与代码 1 不同。答案是“不是很明显”(忽略其他人提到的关于返回值的属性的细微差异。def f(n): return lambda x: x * n__name____code__

答:

1赞 Mous 3/30/2023 #1

如果我们使用 Python 内置的 CodeObject 反汇编器模块,函数中的许多差异是显而易见的。这在 3.11 中会有所不同,但我还没有从 3.10 更新。dis

对于第一个函数

  2           0 LOAD_CLOSURE             0 (n)
              2 BUILD_TUPLE              1
              4 LOAD_CONST               1 (<code object g at 0x000001F59E179000, file "<pyshell#2>", line 2>)
              6 LOAD_CONST               2 ('f.<locals>.g')
              8 MAKE_FUNCTION            8 (closure)
             10 STORE_FAST               1 (g)

  4          12 LOAD_FAST                1 (g)
             14 RETURN_VALUE

Disassembly of <code object g at 0x000001F59E179000, file "<pyshell#2>", line 2>:
  3           0 LOAD_FAST                0 (x)
              2 LOAD_DEREF               0 (n)
              4 BINARY_MULTIPLY
              6 RETURN_VALUE

对于第二个:

  2           0 LOAD_CLOSURE             1 (n)
              2 BUILD_TUPLE              1
              4 LOAD_CONST               1 (<code object g at 0x000001F59E1A52C0, file "<pyshell#5>", line 2>)
              6 LOAD_CONST               2 ('f.<locals>.g')
              8 MAKE_FUNCTION            8 (closure)
             10 STORE_DEREF              0 (g)

  4          12 LOAD_CLOSURE             0 (g)
             14 BUILD_TUPLE              1
             16 LOAD_CONST               3 (<code object <lambda> at 0x000001F59E1A5420, file "<pyshell#5>", line 4>)
             18 LOAD_CONST               4 ('f.<locals>.<lambda>')
             20 MAKE_FUNCTION            8 (closure)
             22 RETURN_VALUE

Disassembly of <code object g at 0x000001F59E1A52C0, file "<pyshell#5>", line 2>:
  3           0 LOAD_FAST                0 (x)
              2 LOAD_DEREF               0 (n)
              4 BINARY_MULTIPLY
              6 RETURN_VALUE

Disassembly of <code object <lambda> at 0x000001F59E1A5420, file "<pyshell#5>", line 4>:
  4           0 LOAD_DEREF               0 (g)
              2 LOAD_FAST                0 (y)
              4 CALL_FUNCTION            1
              6 RETURN_VALUE

每个函数上不同的属性属性是__code__

  • co_cellvars - 'n'也存在于第二个函数中
  • co_code- 第二个函数在返回之前构建一个 lambda;第一个函数直接返回g
  • co_consts- 第二个函数包含两个 CodeObject(以及两个相应的名称),而第一个函数仅包含一个 CodeObject 及其名称
  • co_lines- 该方法在迭代时将返回不同的值,这是不同的直接结果co_linesco_code
  • co_lnotab- (在 3.11 中已弃用)属性是该方法的更节省空间的版本co_lnotabco_lines
  • co_nlocals- 我不确定为什么会有所不同
  • co_varnames - g存在于第一个函数中,因为它在第一个函数中直接引用。但是,在第二个函数中,它仅在 lambda 作用域内被引用,因此出现在 lambda 的自由变量中。f.__code__.co_consts[3].co_freevars

最后,正如 Michael 在注释中指出的那样,所有 lambda 函数(例如此处返回的函数)都是 .__name__'<lambda>'