Is it possible to make multiline functions inside a dictionary?

I’m talking about a feature like this:

obj = {
    "a": def a(b):
        print(b - 1)
        return b + 1
}

And yes I know I can do it with lambda but in more complex scenarios it becomes really hard to just use lambda. Is there a way to make something like this?

The value on the dict must be an expression.
def fn() is a statement.
Which means you cannot do that.

Isn’t there a way to do it? Nearly all languages allow this kind of behavior.

Well, there’s the obvious:

 def a(b):
     print(b - 1)
     return b + 1

 obj = {
     "a": a,
 }

Note that print() is a function. You could:

 obj = {
     "a": lambda b: (
             print(b - 1),
             b + 1,
          )[-1],
 }

Here we’re doing 2 things:

  • writing an extended single expression over multiple lines by embedded
    it in brackets
  • return value of the lambda is the last expression ([-1])

But really, there is a point where the complexity is enough to warrant a
distinct function.

Finally, consider that a class is in a sense a collection of functions
(methods) with names (method names) which are effectively keys, if you
must do things this way. And even without doing oddball “look up a class
method by its name (the key)” you can group things with a class and
maybe then make your dict:

 class C:
     @staticmethod
     def method1(a, b):
         print(a, b)
         return a + b

 obj = {
     "lower": str.lower,
     "ab": C.method1,
 }

Fells a bit gimmicky, but there are probably situations where this is
sensible.

Cheers,
Cameron Simpson cs@cskk.id.au

Yes, but you may want to slightly rethink things. Rather than constructing the dictionary in one go, with the messy indentation levels you have there, try using decorated functions instead:

obj = {}
def thing(f):
    obj[f.__name__] = f
    return f

@thing
def a(b):
    print(b - 1)
    return b + 1

This also avoids the duplication of name with "a" and def a, as each function will be keyed according to its own name.

(By the way, use a better name than @thing for production code. But I don’t know what this is going into, so I can’t suggest a good name.)

Another thing you could do is to use a class block:

class Functions:
    def a(b):
        print(b - 1)
        return b + 1
obj = Functions.__dict__

This does affect scoping though.