Syntax for one-line "nonymous" functions in "declaration style"
Terry Reedy
tjreedy at udel.edu
Wed Mar 27 17:25:35 EDT 2019
On 3/27/2019 4:21 AM, Alexey Muranov wrote:
> Whey you need a simple function in Python, there is a choice between a
> normal function declaration and an assignment of a anonymous function
> (defined by a lambda-expression) to a variable:
>
> def f(x): return x*x
>
> or
>
> f = lambda x: x*x
PEP 8 properly recommends against this the latter as functionally it has
no advantage and the disadvantage that f.__name__ becomes the generic
'<lambda>' instead of the specific 'f'. This is not useful for code
that expects specific names. Tracebacks are one example. Here is
another intended to list callable objects and their types.
def call_clas(container):
for item in vars(container).values():
if hasattr(item, '__call__'):
yield item.__name__, item.__class__
def print_cc(container):
for name, clas in call_clas(container):
print(f'{name:30s}{clas}')
# Examples.
print_cc(int)
from idlelib import pyshell
print_cc(pyshell)
Multiple output lines of '<lambda> function' defeat the
purpose.
So my opinion is that lambda expressions should only be used within
larger expressions and never directly bound.
> It would be however more convenient to be able to write instead just
>
> f(x) = x*x
Given my view above, this is, standing alone, strictly an abbreviation
of the equivalent def statement. I am presuming that a proper
implementation would result in f.__name__ == 'f'.
Is the convenience and (very low) frequency of applicability worth the
inconvenience of confusing the meaning of '=' and complicating the
implementation?
> I do not see any conflicts with the existing syntax.
It heavily conflicts with existing syntax. The current meaning of
target_expression = object_expression
is
1. Evaluate object_expression in the existing namespace to an object,
prior to any new bindings and independent of the target_expression.
2. Evaluate target_expression in the existing namespace to one or more
targets.
3. Bind object to target or iterate target to bind to multiple targets.
Part of step 2 is making calls, because calls cannot be targets. This
is why 'f(a+b)[c] = d' can work. Note that 'a+b' makes calls to
a.__add__ or b.__radd__ or both.
In the proposal, the treatment of the object expression would depend on
the target expression and the alternative would be to quote it as code;
compile the code in a manner that depends on the target expression to
mark local names versus global names; and finally make a function
instance, presumably taking __name__ from the target.
This would have the advantage over lambda assignment of getting the name
right, but I don't think one should be doing lambda assignment anyway.
There is a good reason to have a separate syntax.
Before 3.8, I would stop here and say no to the proposal. But we now
have assignment expressions in addition to assignment statements.
>>> int(s:='42'+'742')
42742
>>> s
'42742'
To me, function assignment expressions, as a enhanced replacement for
lambda expressions, is more inviting than function assignment statements
as an abbreviation for function definition statements.
In other words, replace
map(lambda x: x*x, range(10))
with
map(square(x):=x*x, range(10))
or, if one does not want a specific name,
map(_(x):=x*x, range(10))
Many people dislike lambda expressions, to the point that Guido
considered leaving them out of 3.x. So this replacement might get more
traction. It would make assignment expressions much more useful.
--
Terry Jan Reedy
More information about the Python-list
mailing list