Is there anyway to do something like this:
class A:
def foo(self):
if isinstance(caller, B):
print "B can't call methods in A"
else:
print "Foobar"
class B:
def foo(self, ref): ref.foo()
class C:
def foo(self, ref): ref.foo()
a = A();
B().foo(a) # Outputs "B can't call methods in A"
C().foo(a) # Outputs "Foobar"
Where caller in A uses some form of introspection to determine the class of the calling method's object?
EDIT:
In the end, I put this together based on some of the suggestions:
import inspect
...
def check_caller(self, klass):
frame = inspect.currentframe()
current = lambda : frame.f_locals.get('self')
while not current() is None:
if isinstance(current(), klass): return True
frame = frame.f_back
return False
It's not perfect for all the reasons supplied, but thanks for the responses: they were a big help.
-
2You mention you have loads of classes calling each other. You seem to try to insert some sort of "security" where you restrict which classes are allowed to do what. This is in general a bad idea. Please explain why you want to do this, and maybe somebody can come up with a better solution to the usecase.Lennart Regebro– Lennart Regebro2009年09月30日 12:41:01 +00:00Commented Sep 30, 2009 at 12:41
3 Answers 3
Assuming the caller is a method, then yes you can, by looking in the previous frame, and picking out self from the locals.
class Reciever:
def themethod(self):
frame = sys._getframe(1)
arguments = frame.f_code.co_argcount
if arguments == 0:
print "Not called from a method"
return
caller_calls_self = frame.f_code.co_varnames[0]
thecaller = frame.f_locals[caller_calls_self]
print "Called from a", thecaller.__class__.__name__, "instance"
Üglŷ as heck, but it works. Now why you would want to do this is another question altogether, I suspect that there is a better way. The whole concept of A isn't allowed to call B is likely to be a mistake.
5 Comments
self (use of self is just a convention), etcframe.f_code.co_argcount and see the name of the first argument in frame.f_code.co_varnames[0].The caller is always an instance of A. The fact that you're calling it inside a B method doesn't change that. In other words: Insiode B.foo, ref is an instance of A, so calling ref.foo() is a call on A, B is not involved on that call (it could happen top-level).
The only sane way is to pass a reference to self so A can check if it is B or not.
class A(object):
def foo(self, caller=None):
if isinstance(caller, B):
print "B can't call methods in A"
else:
print "Foobar"
class B(object):
def foo(self, ref): ref.foo(self)
class C(object):
def foo(self, ref): ref.foo(self)
a = A();
B().foo(a) # Outputs "B can't call methods in A"
C().foo(a) # Outputs "Foobar"
a.foo() # Outputs "Foobar"
3 Comments
Something like this may meet your needs better:
class A(object):
def foo(self):
# do stuff
class B(A):
def foo(self):
raise NotImplementedError
class C(A):
pass
...but it's difficult to say without knowing exactly what you're trying to do.