blakef
blakef

Reputation: 23

Can Python determine the class of an object accessing a method

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.

Upvotes: 2

Views: 5309

Answers (3)

Jason Baker
Jason Baker

Reputation: 198567

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.

Upvotes: -1

Lennart Regebro
Lennart Regebro

Reputation: 172239

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.

Upvotes: 6

nosklo
nosklo

Reputation: 222852

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"

Upvotes: 4

Related Questions