Syntax Suggestion: Pass Function Definition as Argument

Chris Angelico rosuav at gmail.com
Fri Nov 8 14:33:09 EST 2019


On Fri, Nov 8, 2019 at 11:22 PM Antoon Pardon <antoon.pardon at vub.be> wrote:
>
> On 8/11/19 13:00, Chris Angelico wrote:
> > On Fri, Nov 8, 2019 at 10:57 PM Antoon Pardon <antoon.pardon at vub.be> wrote:
> >> On 7/11/19 18:10, Stephen Waldron wrote:
> >>> What I'm aiming for is the ability to, within a function call, pass a suite that would be there automatically defined by the compiler/interpreter. Another comment did mention lambda functions, which does to some degree provide that capability, but is restricted to well, lambda functions (only expressions, not statements).
> >> I don't think those restrictions are that limiting. Certainly not since
> >> python3.8 and python acquired an assigment operator. And you should also
> >> note that a list is an expression. So we could do something like the
> >> following.
> >>
> > If you're implying that you can rewrite any function using lambda and
> > a list of expressions, then no, the ":=" operator won't help you:
> >
> >>>> (lambda: x := 1)
> >   File "<stdin>", line 1
> > SyntaxError: cannot use named assignment with lambda
>
> Well I haven't had the time to install python3.8 myself but this is what
> I read in the PEP
>
>     * Unparenthesized assignment expressions are prohibited in lambda
> functions. Example:
>         (lambda: x := 1) # INVALID
>         lambda: (x := 1) # Valid, but unlikely to be useful
>         (x := lambda: 1) # Valid
>         lambda line: (m := re.match(pattern, line)) and m.group(1) # Valid
>
>     This allows lambda to always bind less tightly than :=; having a
>     name binding at the top level inside a lambda function is unlikely
>     to be of value, as there is no way to make use of it. In cases where
>     the name will be used more than once, the expression is likely to
>     need parenthesizing anyway, so this prohibition will rarely affect code.
>
> So it seems you just forgot to use the parenthesis.
>

True, I could make it syntactically valid with parens. But as the
paragraph shows, it's still not possible to use it outside that lambda
function (there's no way to declare it nonlocal), and you can't assign
to anything other than a simple name (no "x[1] := 1"), so you still
can't use this as a true replacement for 'def' functions. I kinda
shorthanded with the example but it's still a very tight limitation.

Going back to looking at the way JavaScript does things, though: One
of the main places that large functions are commonly used as arguments
is asynchronous code. There are better ways to do that (async/await in
both languages, threads and processes in Python), so that's not
necessary. For event driven code, I would much prefer to use 'def'
functions with decorators, rather than function arguments - compare a
bit of Python/Flask code with a similar bit of JS/Express:

@app.route("/foo/<id>")
def foo(id):
    return ...

app.get("/foo/:id", (req, res) => {
    res.send(...)
});

Pretty similar, but one very important distinction is that the Flask
style extends easily to multi-routing, simply by decorating the
function more than once.

For event-driven code (this is for an HTTP server, and the same
applies to a GUI), this is a way cleaner way to do things. I'd rather
look into ways to make decorators do what you need (even if that
involves an enhancement to decorator syntax) than try to create
multi-line lambdas.

ChrisA


More information about the Python-list mailing list