Может ли Python определить класс объекта, обращающегося к методу

Есть ли способ сделать что-то вроде этого:

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"

Где вызывающий в A использует некоторую форму самоанализа для определения класса объекта вызывающего метода?

ИЗМЕНИТЬ:

В конце концов, я собрал это, основываясь на некоторых предложениях:

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

Это не идеально по всем указанным причинам, но спасибо за ответы: они очень помогли.


person blakef    schedule 30.09.2009    source источник
comment
Вы упомянули, что у вас есть множество классов, звонящих друг другу. Кажется, вы пытаетесь вставить какую-то защиту, ограничивая, какие классы могут что делать. Это вообще плохая идея. Пожалуйста, объясните, почему вы хотите это сделать, и, возможно, кто-то сможет придумать лучшее решение для варианта использования.   -  person Lennart Regebro    schedule 30.09.2009


Ответы (3)


Предполагая, что вызывающий объект является методом, тогда да, вы можете, просмотрев предыдущий кадр и выбрав self из местных жителей.

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ŷ черт возьми, но это работает. Теперь, почему вы хотите это сделать, это совсем другой вопрос, я подозреваю, что есть лучший способ. Вся концепция того, что А не может звонить Б, скорее всего, будет ошибкой.

person Lennart Regebro    schedule 30.09.2009
comment
терпит неудачу во многих ситуациях: вызов метода верхнего уровня, вызов метода в свободной функции, использование другого имени для self (использование self — это просто соглашение) и т. д. - person nosklo; 30.09.2009
comment
Ага. Я согласен, что передача вызывающего абонента - лучшее решение. - person Lennart Regebro; 30.09.2009
comment
Вы можете увидеть, есть ли у вызывающей стороны аргументы из frame.f_code.co_argcount, и увидеть имя первого аргумента в frame.f_code.co_varnames[0]. - person Denis Otkidach; 30.09.2009
comment
Хорошая идея! Я обновил метод, чтобы сделать это. Стало еще некрасивее. :) Я все еще думаю, что передача вызывающего абонента - лучшее решение. В ZCA подобные вещи постоянно выполняются между различными компонентами, и вы всегда получаете адаптированный компонент в качестве контекста. Работает отлично. - person Lennart Regebro; 30.09.2009
comment
Это то, что я искал. Это не красиво, но интересно. - person blakef; 30.09.2009

Вызывающий объект всегда является экземпляром A. Тот факт, что вы вызываете его внутри метода B, этого не меняет. Другими словами: Insiode B.foo, ref является экземпляром A, поэтому вызов ref.foo() является вызовом A, B не участвует в этом вызове (это может произойти на верхнем уровне).

Единственный разумный способ - передать ссылку на self, чтобы A мог проверить, является ли он B или нет.

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"
person nosklo    schedule 30.09.2009
comment
Стремление сделать это прозрачно. Это неуправляемо, когда количество методов значительно увеличивается. - person blakef; 30.09.2009
comment
@blakef: я отредактировал свой вопрос, почему это плохая идея. Звонок может произойти на высшем уровне. Попытка определить, где произошел звонок, свидетельствует о неправильном дизайне. Вместо этого вы должны поделиться почему вы хотите, чтобы методы одного класса не вызывали методы другого. - person nosklo; 30.09.2009
comment
В итоге вопрос стал академическим. Ваше решение - лучший подход, но меня интересовало, как это можно сделать без участия кода вызывающего абонента. - person blakef; 30.09.2009

Что-то вроде этого может лучше удовлетворить ваши потребности:

class A(object):
    def foo(self):
        # do stuff

class B(A):
    def foo(self):
        raise NotImplementedError

class C(A):
    pass

... но трудно сказать, не зная точно, что вы пытаетесь сделать.

person Jason Baker    schedule 30.09.2009