0

I know how to get the (unique) address of a function, for example:

def func1():
    return 1

class A:
    def func2():
        return 2

    def func3(self):
        return 3

print('func1:', hex(id(func1)))
print('func2:', hex(id(A.func2)))
print('func3:', hex(id(A.func3)))

But inside any given function, how can I get the address of the caller function, regardless of whether each one of them (the caller and the callee) is a global function, a class function or member function?

I've tried following several answers on similar questions (here for example), most of which seem to suggest using the inspect module.

But they all seem to be asking about the caller function's name rather than its (unique) address, which is what I am really interested in.

Thank you for helping out.

  • 2
    What do you think you're going to do with that information? You're talking about the address of a function object. It's not like you can "jump" to that a0ddress. – Tim Roberts Jun 28 '22 at 20:36
  • @TimRoberts: I will use it as a unique key for a dictionary indicating to me whether or not I have visited this function. –  Jun 28 '22 at 20:38
  • Functions are hashable, so you can call `hash` on a function to get a value that can be used as a key. – mipadi Jun 28 '22 at 20:42
  • I'm not convinced that information is available. The function object is used to find the code to execute. When the interpreter starts executing that code, it doesn't really NEED the function object any more. You can get the file name and the line number from `inspect`; perhaps that's enough. – Tim Roberts Jun 28 '22 at 20:44
  • @mipadi: Will that give me a unique hash for two functions of the same name residing in different modules (i.e., files or classes)? Even if yes - how can I do that for **the caller function**? I still need to get a "handle" to that function, don't I? I don't care whether it's the `id` or something else, but I still need to have a pointer to the actual function as far as I can tell. –  Jun 28 '22 at 20:45
  • @TimRoberts: I've noticed that `inspect` functions actually return information which contains addresses, therefore, I suspect that the information which I have asked for is indeed available. –  Jun 28 '22 at 20:46
  • You have the address of the frame object. I don't know that it contains the calling function object. As I said, it might not be knowable. – Tim Roberts Jun 28 '22 at 20:48
  • @TimRoberts: Perhaps I should hash the entire frame then (as a string, I suppose)? The question is, what frame should I grab in order for it to give me the caller function? –  Jun 28 '22 at 20:52
  • @bbbbbbbbb no need, **just use the function object itself**. It will hash based on identity anyway – juanpa.arrivillaga Jun 28 '22 at 20:53
  • @bbbbbbbbb it will give you a unique hash based on the object identity. Function object equality will work the same. You should really just elaborate on exactly what you are trying to accomplish, there is almost certainly a straightforward approach. – juanpa.arrivillaga Jun 28 '22 at 20:54
  • `inspect.stack()[1]` gives you the caller frame. But frame objects aren't unique. You might get calls from two different places with the same frame object, and depending on the activity, the same call might get a different frame object. – Tim Roberts Jun 28 '22 at 20:55
  • And note, it really isnt strictly correct to talk about "addresses" here. The `id` function simply returns an `int` which is *guaranteed unique during the lifetime of an object*. The fact that it happens to use the address of the PyObject header of a Python object struct in CPython is an implementation detail, and it works differently in different implementations (e.g. Jython) – juanpa.arrivillaga Jun 28 '22 at 20:55
  • @TimRoberts more importantly, I think frame objects are pretty ephemeral, and will likely re-use `id`'s despite being different objects – juanpa.arrivillaga Jun 28 '22 at 20:56
  • Anyway, I think what you really want to be able to do is to *retrieve the caller function itself*, then you would just keep a `set` around to see if you've visited that function already. But it would be helpful if you elaborated about your actual use-case – juanpa.arrivillaga Jun 28 '22 at 20:59
  • @juanpa.arrivillaga: Yup, but I'd still need a **unique identifier** of that function. You can assume that all the objects in my code "live forever" (i.e., they are all global). –  Jun 28 '22 at 21:02
  • @bbbbbbbbb *the function itself can serve as a unique identifier*. In any case, how else would you construct this identifier without the function object? You don't want a dict, mapping `{: }`, you just want a set, `{}`. – juanpa.arrivillaga Jun 28 '22 at 21:02
  • @juanpa.arrivillaga: So how do I can the caller function **object** then? –  Jun 28 '22 at 21:03
  • @bbbbbbbbb now *that* is an interesting question. I found [this relevant question](https://stackoverflow.com/questions/39078467/python-how-to-get-the-calling-function-not-just-its-name) but it relies on pretty desperate heuristics that are only useful in the context of that asker's specific use-case AFAIKT. Maybe, you might be able to just use the `func.__code__` object, because I believe that is relatively straight-forwardly retrievable (in which case, you could use a dict `{:}` if you need to retrieve the actual function object, otherwise a set if you just need to know – juanpa.arrivillaga Jun 28 '22 at 21:06
  • @juanpa.arrivillaga -- Those are all good words, but I don't think there is an answer to his fundamental question. You can get the NAME of the function that called you, and you can get the filename and line number of the source line that called you, but I'm not aware of any path to get the function object of any ancestor functions. – Tim Roberts Jun 28 '22 at 22:19

0 Answers0