22

How do I introspect A's instance from within b.func() (i.e. A's instance's self):

class A(): def go(self): b=B() b.func() class B(): def func(self): # Introspect to find the calling A instance here 
2
  • Note that in Python 2.x, it is best to inherit from object rather than nothing so that you are using new-style classes. Commented Sep 1, 2011 at 15:18
  • There is no portable way to do this (the only way to do it in CPython is by grabbing the parent frame and inspecting it, but not all Python implementations expose this data) Commented Sep 1, 2011 at 15:42

4 Answers 4

44

In general we don't want that func to have access back to the calling instance of A because this breaks encapsulation. Inside of b.func you should have access to any args and kwargs passed, the state/attributes of the instance b (via self here), and any globals hanging around.

If you want to know about a calling object, the valid ways are:

  1. Pass the calling object in as an argument to the function
  2. Explicitly add a handle to the caller onto b instance sometime before using func, and then access that handle through self.

However, with that disclaimer out of the way, it's still worth knowing that Python's introspection capabilities are powerful enough to access the caller module in some cases. In the CPython implementation, here is how you could access the calling A instance without changing your existing function signatures:

class A: def go(self): b=B() b.func() class B: def func(self): import inspect print inspect.currentframe().f_back.f_locals["self"] if __name__ == "__main__": a = A() a.go() 

Output:

<__main__.A instance at 0x15bd9e0> 

This might be a useful trick to know about for debugging purposes. A similar technique is even used in stdlib logging, here, so that loggers are able to discover the source code/file name/line number/function name without needing to be explicitly passed that context. However, in normal use cases, it would not usually be a sensible design decision to access stack frames in the case that B.func actually needed to use A, because it's cleaner and easier to pass along the information that you need rather than to try and "reach back" to a caller.

Sign up to request clarification or add additional context in comments.

15 Comments

This is fragile (there are plenty of cases we could construct with which it does not work) and non-portable (inspect.currentframe is an implementation detail).
+1 for answering the question as asked ( regardless of whether it is wise to do it this way ). Where will this not work? I suspect the "disclaimer" about currentframe() is alluding to Stackless ( not to Jython or Iron Python )....can anyone confirm this?
@Mike you're missing the point, i'm not saying you rewrite you code instead of pdb. suppose you are in ipython, and you get an unhandled exception in B.func. then you can use the magic %debug and you're in the body of func. from there you have the full power of the interpreter, and you could type import inspect and do whatever introspection might help you. just because a tool can be abused it doesn't mean it should never be used
@wim Actually, it's up to the community whether this knowledge is helpful to more than just the poster. That's why it's getting downvotes. These are a signal that what you posted is a poor solution for the OP to use.
I find this all astonishing. Do people think that Python's ability to do introspection should be kept secret just because it's usually not the right thing to do? Only one answer gave the right facts, and people downvote it? Facts are facts. Do the downvoters object to revealing this "secret knowlege"? You all have no idea why the OP is looking for this, and therefore you can not judge that it is wrong. There are certainly possible situations where this type of introspection would be valuable. So, imho, ok to warn against it, but should not downvote ans for revealing how it can be done.
|
5

You pass it to b.func() as an argument.

9 Comments

That was my workaround, however I wanted to find out how to refrain from that
That's not a workaround. That's how you give other functions context.
I already implemented it your way and carried on, but I'm curios to know... besides I think that in my specific design it might be more elegant
What is your specific design? I seriously doubt it, though. "Explicit is better than implicit"
agreed. technically you can get the caller, but b.func() really has no business knowing who called it
|
1

Do this by refactoring your code to work like

class A(): def go(self): b = B(self) b.func() class B(): def __init__(self, a): self.a = a def func(self): # Use self.a 

or

class A(): def go(self): b = B() b.func(self) class B(): def func(self, a): # a 

2 Comments

Actually there are many As that may call B's instance. I wouldn't like to keep them all in B's instance
@Johnathan, In the example snippet, Bs were made by an A, and it wasn't clear they were kept and used by many different As. It sounds like you may want to pass the right A precisely when you cal B.func, like in my second example.
0

I agree with Benjamin - pass it to b.func() as an argument and don't introspect it!!!!

If your life really depends on it, then I think you can deduce the answer from this answer.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.