2010-02-04 11 views

Répondre

16

Je ne crois pas que, au niveau de l'objet frame, il y ait un moyen de trouver l'objet réel de la fonction python qui a été appelé.

Cependant, si votre code repose sur la convention commune: nommer le paramètre d'instance d'une méthode self, vous pouvez effectuer les opérations suivantes:

def get_class_from_frame(fr): 
    import inspect 
    args, _, _, value_dict = inspect.getargvalues(fr) 
    # we check the first parameter for the frame function is 
    # named 'self' 
    if len(args) and args[0] == 'self': 
    # in that case, 'self' will be referenced in value_dict 
    instance = value_dict.get('self', None) 
    if instance: 
     # return its class 
     return getattr(instance, '__class__', None) 
    # return None otherwise 
    return None 

Si vous ne souhaitez pas utiliser getargvalues, vous pouvez utiliser directement frame.f_locals au lieu de value_dict et frame.f_code.co_varnames[:frame.f_code.co_argcount] au lieu de args.

Gardez à l'esprit que cela est encore se fondant uniquement sur la convention, il est donc pas portable, et sujette aux erreurs:

  • si une utilisation de la fonction non-méthode self comme premier nom de paramètre, get_class_from_frame sera à tort retourne la classe du premier paramètre.
  • il peut être trompeur lorsque vous travaillez avec des descripteurs (il renverra la classe du descripteur, pas de l'instance en cours d'accès).
  • @classmethod et @staticmethod ne prend pas un paramètre self et sont implémentés avec des descripteurs.
  • et sûrement beaucoup plus

Selon exactement ce que vous voulez faire, vous voudrez peut-être prendre un certain temps pour creuser plus profondément et de trouver des solutions de contournement pour toutes ces questions (vous pouvez vérifier la fonction de cadre existent dans la retourné la classe et partager la même source, la détection des appels descripteur est possible, même pour les méthodes de classe, etc.)

+0

Merci pour votre réponse, Clément; c'est détaillé et réfléchi. J'espère toujours obtenir plus qu'une seule réponse! Mais sinon, celui-ci devra suffire ... –

+0

OK, attendu assez longtemps. C'est une bonne réponse! J'avais espéré un meilleur soutien des structures de cadre, mais c'est ce que c'est. Merci encore, Clément! –

+1

Pour en ajouter un à la liste, veillez à ce que 'self' soit une instance d'une classe dérivée, qui pourrait ne pas être celle qui nous intéresse. – gertjan

7

Ceci est un peu plus court, mais fait à peu près la même chose. Renvoie None si le nom de la classe n'est pas disponible.

def get_class_name(): 
    f = sys._getframe(1) 

    try: 
     class_name = f.f_locals['self'].__class__.__name__ 
    except KeyError: 
     class_name = None 

    return class_name 
+0

Bonne réponse! Mieux encore, vous pourriez faire '' f.f_locals ['__ class __'] .__ name__'', qui devrait aussi couvrir les cas '' @ classmethod'' et '' @ staticmethod''. –

+2

@LucioPaiva qui ne fonctionne pas. Il n'y a pas de clé "__class __" dans f_locals. – Pithikos

2

Je suis juste tombé sur ce post car j'étais confronté au même problème. Cependant, je ne considérais pas la méthode «self» comme une solution acceptable pour toutes les raisons déjà mentionnées.

Le code suivant illustre une approche différente: étant donné un objet frame, il recherche les globals pour un objet avec un nom de membre correspondant et un bloc de code. La recherche n'est pas exhaustive, il est donc possible que toutes les classes ne soient pas découvertes, mais les classes trouvées devraient être celles que nous recherchons parce que nous vérifions les codes correspondants.

objet du code est de préfixer un nom de fonction avec son nom de classe, se trouve:

def get_name(frame): 
    code = frame.f_code 
    name = code.co_name 
    for objname, obj in frame.f_globals.iteritems(): 
    try: 
     assert obj.__dict__[name].func_code is code 
    except Exception: 
     pass 
    else: # obj is the class that defines our method 
     name = '%s.%s' % (objname, name) 
     break 
    return name 

Notez l'utilisation de __dict__ au lieu de getattr pour empêcher la capture des classes dérivées. Notez également qu'une recherche globale peut être évitée si self = frame.f_locals['self']; obj = self.__class__ donne une correspondance, ou obj in self.__class__.__bases__ ou plus profond, donc il y a certainement de la place pour l'optimisation/l'hybridation.

1

Si une méthode est une méthode de classe, la classe sera le premier argument. Cela affiche le type du premier argument si présent pour chaque trame de pile appelante:

def some_method(self): 
     for f in inspect.getouterframes(inspect.currentframe()): 
      args, _,_, local_dict = inspect.getargvalues(f[0]) 
      if args: 
       first_arg = args[0] 
       first_value = local_dict[first_arg] 
       print(type(first_value).__name__)