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?

1 Like

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

1 Like

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

1 Like

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

1 Like

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.

3 Likes