Is there a difference (even subtle) between those two following codes? (is there any special use case of second code?)
def f(n):
def g(x):
return x * n
return g
def f(n):
def g(x):
return x * n
return lambda y: g(y)
If we use dis
, Python's builtin CodeObject disassembler module, many differences are apparent in the functions. This will be different in 3.11, but I have not updated from 3.10.
For the first function
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
And for the second:
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
The attributes of the __code__
attribute on each function that are different are
co_cellvars
- 'n'
is also present in the second functionco_code
- the second function builds a lambda before returning; the first function returns g
directlyco_consts
- the second function contains two CodeObjects (and two corresponding names), whereas the first only contains one CodeObject with its nameco_lines
- The co_lines
method will return different values when iterated over as a direct consequence of co_code
being differentco_lnotab
- The (deprecated in 3.11) co_lnotab
attribute is a more space-efficient version of the co_lines
methodco_nlocals
- I'm not sure why this differsco_varnames
- g
is present in the first function, because it's directly referenced in the first function. However in the second function it's only referenced within the lambda scope, and so occurs in f.__code__.co_consts[3].co_freevars
, the free variables of the lambda instead.Finally, as Michael notes in the comments, the __name__
of all lambda functions, such as those returned here, is '<lambda>'
.