2009-09-30 7 views
2

Y at-il de toute façon de faire quelque chose comme ceci:Peut-Python déterminer la classe d'un objet qui accède à une méthode

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" 

appelant en A utilise une forme d'introspection pour déterminer la classe de l'objet de la méthode d'appel ?

EDIT:

En fin de compte, je mis cela ensemble en fonction de quelques-unes des 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 

Ce n'est pas parfait pour toutes les raisons fournies, mais merci pour les réponses: ils étaient une grande aide.

+2

Vous mentionnez que vous avez des tas de classes qui s'appellent l'une l'autre. Vous semblez essayer d'insérer une sorte de "sécurité" où vous restreignez quelles classes sont autorisées à faire quoi. C'est en général une mauvaise idée. S'il vous plaît expliquer pourquoi vous voulez faire cela, et peut-être que quelqu'un peut trouver une meilleure solution à l'usecase. –

+2

ur le faire rong – Triptych

Répondre

6

En supposant que l'appelant est une méthode, alors oui vous pouvez, en regardant dans le cadre précédent, et la cueillette self de la population locale.

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" 

Oui, mais cela fonctionne. Maintenant pourquoi vous voudriez faire ceci est une autre question complètement, je soupçonne qu'il y a une meilleure manière. Le concept entier de A n'est pas autorisé à appeler B est susceptible d'être une erreur.

+2

échoue dans beaucoup de situations: appeler la méthode de haut niveau, appeler la méthode dans une fonction libre, utiliser un autre nom pour 'self' (l'utilisation de' self' est juste une convention), etc – nosklo

+0

Yup. Je suis d'accord que le passage de l'appelant est une meilleure solution. –

+1

Vous pouvez voir si l'appelant a des arguments de 'frame.f_code.co_argcount' et voir le nom du premier argument dans' frame.f_code.co_varnames [0] '. –

4

L'appelant est toujours une instance de A. Le fait que vous l'appeliez dans une méthode B ne change pas cela. En d'autres termes: Insiode B.foo, ref est une instance de A, donc appelant ref.foo() est un appel sur A, B n'est pas impliqué sur cet appel (il pourrait arriver de haut niveau).

Le seul moyen consiste à passer une référence à self afin que A puisse vérifier s'il est B ou non.

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" 
+0

Vous cherchez à le faire de manière transparente. C'est ingérable quand le nombre de méthodes augmente significativement. – blakef

+6

@blakef: J'ai modifié ma question pour savoir pourquoi c'est une mauvaise idée. L'appel pourrait arriver au plus haut niveau. Essayer de détecter où l'appel s'est passé signale une mauvaise conception. Vous devriez plutôt partager ** pourquoi diable ** vous voulez empêcher les méthodes d'une classe d'appeler des méthodes sur une autre. – nosklo

+0

La question est devenue académique à la fin. Votre solution est la meilleure approche, mais j'étais intéressé par la façon dont cela pourrait être fait sans impliquer le code de l'appelant. – blakef

0

Quelque chose comme cela peut répondre à vos besoins mieux:

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

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

class C(A): 
    pass 

... mais il est difficile de dire sans savoir exactement ce que vous essayez de faire.

Questions connexes