Let me also throw in my two cents about different workarounds.
How is a simple one-line lambda different from a normal function? I can think only of lack of assignments, some loop-like constructs (for, while), try-except clauses... And that's it? We even have a ternary operator - cool! So, let's try to deal with each of these problems.
Assignments
Some guys here have rightly noted that we should take a look at lisp's let
form, which allows local bindings. Actually, all the non state-changing assignments can be performed only with let
. But every lisp programmer knows that let
form is absolutely equivalent to call to a lambda function! This means that
(let ([x_ x] [y_ y])
(do-sth-with-x-&-y x_ y_))
is the same as
((lambda (x_ y_)
(do-sth-with-x-&-y x_ y_)) x y)
So lambdas are more than enough! Whenever we want to make a new assignment we just add another lambda and call it. Consider this example:
def f(x):
y = f1(x)
z = f2(x, y)
return y,z
A lambda version looks like:
f = lambda x: (lambda y: (y, f2(x,y)))(f1(x))
You can even make the let
function, if you don't like the data being written after actions on the data. And you can even curry it (just for the sake of more parentheses :) )
let = curry(lambda args, f: f(*args))
f_lmb = lambda x: let((f1(x),), lambda y: (y, f2(x,y)))
# or:
f_lmb = lambda x: let((f1(x),))(lambda y: (y, f2(x,y)))
# even better alternative:
let = lambda *args: lambda f: f(*args)
f_lmb = lambda x: let(f1(x))(lambda y: (y, f2(x,y)))
So far so good. But what if we have to make reassignments, i.e. change state? Well, I think we can live absolutely happily without changing state as long as task in question doesn't concern loops.
Loops
While there's no direct lambda alternative for loops, I believe we can write quite generic function to fit our needs. Take a look at this fibonacci function:
def fib(n):
k = 0
fib_k, fib_k_plus_1 = 0, 1
while k < n:
k += 1
fib_k_plus_1, fib_k = fib_k_plus_1 + fib_k, fib_k_plus_1
return fib_k
Impossible in terms of lambdas, obviously. But after writing a little yet useful function we're done with that and similar cases:
def loop(first_state, condition, state_changer):
state = first_state
while condition(*state):
state = state_changer(*state)
return state
fib_lmb = lambda n:\
loop(
(0,0,1),
lambda k, fib_k, fib_k_plus_1:\
k < n,
lambda k, fib_k, fib_k_plus_1:\
(k+1, fib_k_plus_1, fib_k_plus_1 + fib_k))[1]
And of course, one should always consider using map
, reduce
and other higher-order functions if possible.
Try-except and other control structs
It seems like a general approach to this kind of problems is to make use of lazy evaluation, replacing code blocks with lambdas accepting no arguments:
def f(x):
try: return len(x)
except: return 0
# the same as:
def try_except_f(try_clause, except_clause):
try: return try_clause()
except: return except_clause()
f = lambda x: try_except_f(lambda: len(x), lambda: 0)
# f(-1) -> 0
# f([1,2,3]) -> 3
Of course, this is not a full alternative to try-except clause, but you can always make it more generic. Btw, with that approach you can even make if
behave like function!
Summing up: it's only natural that everything mentioned feels kinda unnatural and not-so-pythonically-beautiful. Nonetheless - it works! And without any evals
and other trics, so all the intellisense will work. I'm also not claiming that you shoud use this everywhere. Most often you'd better define an ordinary function. I only showed that nothing is impossible.
def
can not line inline with the logic it is intended for use: you have to go place it somewhere else and then the reader has to go hunt for it. Having adef
for code that is only used once is a serious deficiency with the python language: those should only be needed for code re-use. – WestCoastProjects