Reputation: 48
I have been learning about Python functions and functions in general, and I came across this idea of anonymous functions, whose advantages, among others, are apparently the ability to keep the namespaces clean as well as not allocating extra memory because a function is only executed once it is assigned to a variable.
In Python, from what I understood, the only way to create anonymous functions is to wrap them in another function. So I came up with the idea of creating a single container for multiple anonymous functions in the code and addressing them through the selector which is essentially calling the wrapper with a parameter:
def anonwrap(selector):
if selector == "addition":
def anon(param1, param2):
return param1 + param2
return anon
elif selector == "the meaning of life":
def anon(param1):
return param1 + " 42"
return anon
else:
def anon(*args, **kwargs):
print("no idea")
return anon
select = anonwrap("addition")
print(select(10, 20))
select = anonwrap("the meaning of life")
print(select("the meaning of life is"))
select = anonwrap("hello")
print(select("blah", 9001))
My question is, once the anonwrap
function gets defined in the code, does the interpreter automatically allocate memory for all the inner functions, or does it only allocate memory for a specific inner function once it gets called from the main code?
How effective is this code at all?
Upvotes: 3
Views: 214
Reputation: 152775
As far as I can see it Python automatically creates code objects for all inner functions and saves them as constants:
>>> anonwrap.__code__.co_consts
(None,
'addition',
<code object anon at 0x0000022BB354DD20, file "<ipython-input-78-ab41b0534822>", line 3>,
'anonwrap.<locals>.anon',
'the meaning of life',
<code object anon at 0x0000022BB354D780, file "<ipython-input-78-ab41b0534822>", line 7>,
<code object anon at 0x0000022BB354DE40, file "<ipython-input-78-ab41b0534822>", line 11>)
But it only creates a function (MAKE_FUNCTION
opcode) when the appropriate branch is "hit" when calling anonwrap
:
import dis
dis.dis(anonwrap)
2 0 LOAD_FAST 0 (selector)
2 LOAD_CONST 1 ('addition')
4 COMPARE_OP 2 (==)
6 POP_JUMP_IF_FALSE 20
3 8 LOAD_CONST 2 (<code object anon at 0x0000022BB3434A50, file "<ipython-input-74-bb454d2da558>", line 3>)
10 LOAD_CONST 3 ('anonwrap.<locals>.anon')
12 MAKE_FUNCTION 0
14 STORE_FAST 1 (anon)
5 16 LOAD_FAST 1 (anon)
18 RETURN_VALUE
6 >> 20 LOAD_FAST 0 (selector)
22 LOAD_CONST 4 ('the meaning of life')
24 COMPARE_OP 2 (==)
26 POP_JUMP_IF_FALSE 40
7 28 LOAD_CONST 5 (<code object anon at 0x0000022BB354DC00, file "<ipython-input-74-bb454d2da558>", line 7>)
30 LOAD_CONST 3 ('anonwrap.<locals>.anon')
32 MAKE_FUNCTION 0
34 STORE_FAST 1 (anon)
9 36 LOAD_FAST 1 (anon)
38 RETURN_VALUE
11 >> 40 LOAD_CONST 6 (<code object anon at 0x0000022BB354DC90, file "<ipython-input-74-bb454d2da558>", line 11>)
42 LOAD_CONST 3 ('anonwrap.<locals>.anon')
44 MAKE_FUNCTION 0
46 STORE_FAST 1 (anon)
13 48 LOAD_FAST 1 (anon)
50 RETURN_VALUE
52 LOAD_CONST 0 (None)
54 RETURN_VALUE
Personally I would say that the code itself is not very effective, neither for future maintenance nor for performance. Creating the code objects is only done once but converting (or compiling - not sure on the language here) these into functions objects is probably a bit expensive.
One additional comment: If you want to keep the namespace clean you normally use submodules (or even classes) to "bundle" the functions. Inner functions (or as you call them "anonymous functions") are mostly used for closures (for example decorators).
Upvotes: 2