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?

MSeifert
  • 145,886
  • 38
  • 333
  • 352
Regardless
  • 48
  • 3
  • 2
    I believe what you are looking for when you say `anonymous` functions would be `lambda` as discussed[here](http://www.diveintopython.net/power_of_introspection/lambda_functions.html) – Professor_Joykill Aug 08 '17 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. – Regardless Aug 08 '17 at 13:28
  • Also just out of curiosity, why does the third `anon` function `print` instead of `return` `"no idea"` – Professor_Joykill Aug 08 '17 at 13:37
  • Because the last line of code should not have contained the print statement just for the showoff sake, but it did due to my inadvertence. – Regardless Aug 08 '17 at 13:47
  • 2
    I think you are looking for modules. They are made to keep the namespaces clean without all that cruft. – Martin Aug 08 '17 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}` – GH05T Aug 08 '17 at 14:46
  • Correct, but the else case would not work out because it holds a print statement. – Regardless Aug 08 '17 at 14:53

1 Answers1

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

MSeifert
  • 145,886
  • 38
  • 333
  • 352
  • 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 Aug 08 '17 at 14:20
  • 1
    @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. – MSeifert Aug 08 '17 at 14:43
  • @Regardless Please don't forget to [accept](https://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work) 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. – MSeifert Aug 08 '17 at 18:27
  • Done. I am fairly new to StackOverflow, so I was not sure how this works. And thanks again. – Regardless Aug 08 '17 at 21:18