1

Sorry if this is very lame, but I'm pretty new to Python.

As in Python everything is an object, I assume in every object the object itself can be get somehow. In object methods the self variable contains it. From the object reference the class object can be get (like type(self)). But how this could be got inside a lambda?

I could figure out for a normal function:

import inspect

def named_func():
    func_name = inspect.stack()[0].function
    func_obj = inspect.stack()[1].frame.f_locals[func_name]
    print(func_name, func_obj, func_obj.xxx)

named_func.xxx = 15
named_func()

The output looks like this:

named_func <function named_func at 0x7f56e368c2f0> 15

Unfortunately in a lambda the inspect.stack()[0].function gives <lambda> inside the lambda.

Is there a way to get the function object inside a lambda? Is there a way to get function object directly (not using the name of the function)? I imagined __self__, but it does not work.

UPDATE

I tried something like this in lambda:

lambda_func = lambda : inspect.stack()[0]
lambda_func.xxx = 2
print(lambda_func())

This prints:

FrameInfo(frame=<frame at 0x7f3eee8a6378, file './test_obj.py', line 74, code <lambda>>, filename='./test_obj.py', lineno=74, function='<lambda>', code_context=['lambda_func = lambda : inspect.stack()[0]\n'], index=0)

But for example is there a way to get the lambda object field xxx in this case? For this the lambda object should be got somehow.

TrueY
  • 7,360
  • 1
  • 41
  • 46
  • 1
    Can you give a [mre] of your lambda? – MattDMo Jan 14 '22 at 18:14
  • Does this answer your question? [How to get current function into a variable?](https://stackoverflow.com/questions/4492559/how-to-get-current-function-into-a-variable) – flakes Jan 14 '22 at 18:16
  • 4
    Are you doing this just for learning, or do you have a practical use case? – 0x5453 Jan 14 '22 at 18:19
  • @flakes The cited question is about how to get the object inside a normal function. This is my example code. But I would like to go a step forward to do similar inside a lambda function. – TrueY Jan 14 '22 at 18:46
  • @0x5453 Does it matter? :) Actually, I would like to get the function's property without referring to the name of the function. So, if I rename the function, I do not have consider change inside the function. – TrueY Jan 14 '22 at 18:49
  • 2
    The PEP for this was [specifically rejected](https://www.python.org/dev/peps/pep-3130/). Just use a named function, and use the name. – user2357112 Jan 14 '22 at 19:08
  • `self` is just a parameter whose argument is set by the `method` instance that wraps the function used to define it. A lambda expression *already* defines a "normal function"; there is no fundamental difference between objects defined by `def` statements and objects defined by lambda expressions. – chepner Jan 14 '22 at 19:14
  • @user2357112 supports Monica Thx for the PEP! It is sad that this was refused. Actually (IMHO) it should be extended by __lambda__ or __function__ would do the job. For me it is strange that this was not implemented in the original version of the language... – TrueY Jan 15 '22 at 19:24
  • @chepner You are absolutely right about this! Used as to show that there are some possibilities, but there are a lot of missing ones. But as the rejected PEP shows, it was not just my idea how this should work to be more complete. – TrueY Jan 15 '22 at 19:26

2 Answers2

1

We can now use a new python syntax to make it shorter and easier to read, without the need to define a new function for this purpose.

You can find two examples below:

Fibonacci:

(f:=lambda x: 1 if x <= 1 else f(x - 1) + f(x - 2))(5)

Factorial:

(f:=lambda x: 1 if x == 0 else x*f(x - 1))(5)

We use := to name the lambda: use the name directly in the lambda itself and call it right away as an anonymous function.

So in your particular use-case it would give something like that:

print((f:=lambda: f.__hash__())())  # prints the hash for example

You can do whatever you want with that f variable now (inside the lambda).

But in fact, if you don't mind multi-lines for your code, you could also just use the name directly and do something like that:

f = lambda : f.xxx
f.xxx = 2
print(f())

(see https://www.python.org/dev/peps/pep-0572 for more information about this := operator)

gruvw
  • 725
  • 8
  • 14
  • This is not a duplicate answer! I added a lot more details and explained the specific use case of the user. I just use two examples coming from another question here: https://stackoverflow.com/a/70731695/14580601 – gruvw Jan 17 '22 at 17:35
-1

Note, this is not an efficient/pragmatic solution to the problem. This is not a recommendation about how to write actual software.. it simply presents how access to the lambda reference from the lambda can be achieved without assigning it to a variable name. This is a very hacky answer.

This will only work completely correctly if you follow the advice from the answer found here

In short, given the stack you can find the code object, and then using the gc module you can find the reference to your lambda.

Example with @Tomalak's factorial lambda!

import gc
import inspect


def current_lambda():
    lambda_code = inspect.stack()[1].frame.f_code
    candidates = [
        referrer
        for referrer in gc.get_referrers(lambda_code)
        if inspect.isfunction(referrer)
        and referrer.__code__ is lambda_code
    ]
    if len(candidates) != 1:
        raise ValueError(
            "Multiple candidates found! Cannot determine correct function!"
        )
    return candidates[0]


print((lambda n: 1 if n < 2 else n * current_lambda()(n - 1))(5))

Outputs

120

Revisiting your example:

lambda_func = lambda: current_lambda().xxx
lambda_func.xxx = 10
print(lambda_func())

Outputs:

10
flakes
  • 21,558
  • 8
  • 41
  • 88
  • This is great! Thanks! I used this `lfunc = lambda : gc.get_referrers(inspect.stack()[0].frame.f_code)[0]` and now `print(lfunc)` and `print(lfunc())` gives the same result: ` at 0x7f8305a02840>`! Super! And this also works for normal functions as well! – TrueY Jan 14 '22 at 19:32
  • This is **absurdly** slow, since it requires a search through literally every single GC-tracked object in the entire program. That means the issue won't be obvious in a toy example program, but once you've got more objects, this rapidly becomes unusable, even if those objects have nothing to do with the lambda you're looking for. – user2357112 Jan 14 '22 at 22:38
  • It also fails if multiple functions are using the same code object, which happens consistently in some cases with closures, but can also happen inconsistently in multi-threaded programs, leading to hard-to-reproduce bugs. – user2357112 Jan 14 '22 at 22:49
  • @flakes I applied this to a class-object and I got back a function-object for my surprise! It is like: `class X(): print(gc.get_referrers(inspect.stack()[0].frame.f_code)[0])`. This gives <`function X at 0x7fb7f1c94048>`. – TrueY Jan 15 '22 at 18:46
  • 1
    @user2357112 supports Monica You are right! There are issues with this approach. But my question was about if this is possible and it is! :) IMHO this is a (not the biggest) problem of Python that it states that everything is an object, but sometimes it is rather difficult to get the "self" pointer of a lambda or of a class inside the class body. Maybe some kind of automatism (like `__self__`) could be implemented to support this. But maybe I'm wrong... – TrueY Jan 15 '22 at 18:51