3

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?

7
  • 2
    I believe what you are looking for when you say anonymous functions would be lambda as discussedhere Commented Aug 8, 2017 at 13:24
  • 1
    Thank you for your reply. I am aware of lambdas. Unfortunately, lambdas in Python are limited to a single expression and they do not accept using statements or including other functions into their body. Commented Aug 8, 2017 at 13:28
  • Also just out of curiosity, why does the third anon function print instead of return "no idea" Commented Aug 8, 2017 at 13:37
  • 2
    I think you are looking for modules. They are made to keep the namespaces clean without all that cruft. Commented Aug 8, 2017 at 13:49
  • 1
    This is a simple example of a dictionary with some anonymous function as values returned for each key: d = {'addition': lambda x: x+2, 'the meaning of life': lambda y: y+42} Commented Aug 8, 2017 at 14:46

1 Answer 1

2

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).

Sign up to request clarification or add additional context in comments.

4 Comments

The idea behind me trying to use inner functions was to get as close as possible to having truly anonymous functions in other languages like JS or Go: myvar = function(args) {statement1; statement2; return value} thus I called them anonymous, which they kind of are. Looks like Python is somewhat deficient in this capability. Your answer was absolutely thorough. Much appreciated.
@Regardless Languages that optimize the code based on introspection definetly have an advantage here because they might realize that the functions could be compiled ahead of time because there's no closure. However, I may have exaggerated a bit: Compiling a function object from a code object isn't slow, it's just not as fast. But there are use-cases for these inner functions in Python - but I wouldn't use them in your case. You could achieve the same with much simpler means here.
@Regardless Please don't forget to accept the answer if it solved your issue. If it doesn't solve your question (or you want to wait for additional answers) feel free to leave it unanswered.
Done. I am fairly new to StackOverflow, so I was not sure how this works. And thanks again.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.