Short answer: No, Python lambdas were not designed to do that.
Longer answer #1: pass the function in as the first argument to itself.
>>> (lambda f: lambda x: f(f,x))(lambda self, x: 1 if x<=1 else x*self(self, x-1))(5)
120
Get it? No? Let's unpack this.
def save_func(self):
def call_with_self_and(x):
return self(self, x)
return call_with_self_and
@save_func
def factorial(self, x):
return 1 if x<=1 else x*self(self, x-1)
factorial(5) # 120
It's the same as the one-liner, but I gave everything names. Lambdas have no way to refer to themselves in Python, but you can pass in the lambda as its own first argument. That's what call_with_self_and()
does for you: assuming you already have a self, it calls self(self, x)
, while only requiring x
as an argument. That's the function you want.
And save_func()
is the factory function that creates the function you want out of the function you have (almost). It takes the self
and returns the call_with_self_and()
.
The "almost" part is that the function you have needs a tweak, from this:
lambda x: 1 if x <= 1 else x*self(x-1)
To this:
lambda self, x: 1 if x<=1 else x*self(self, x-1)
See what I did there? I passed self
in as the first argument to self
when recursing.
The search term you're looking for is "Y combinator".
Longer answer #2: Python lambdas were not designed to do that, but we can implement it.
This helper function can give a lambda an explicit recursion parameter.
def R(f):
def self(*a,**kw):
return f(self, *a, **kw)
return self
Use it like so:
print(R(lambda self, x: 1 if x <= 1 else x*self(x-1))(5))