2010-02-04 10 views

Antwort

16

Ich glaube nicht, dass es auf der Frame-Objekt-Ebene gibt es eine Möglichkeit, das tatsächliche Python-Funktionsobjekt zu finden, die aufgerufen wurde.

Allerdings, wenn Ihr Code über die gemeinsame Konvention verlassen: die Instanz Parameter einer Methode zu benennen self, dann könnten Sie Folgendes tun:

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 

Wenn Sie getargvalues nicht verwenden, können Sie Verwenden Sie direkt frame.f_locals statt value_dict und frame.f_code.co_varnames[:frame.f_code.co_argcount] anstelle von args.

Beachten Sie, dass dies immer noch nur auf Konvention zu verlassen, so ist es nicht tragbar, und fehleranfällige:

  • , wenn eine Nicht-Methode Funktion Verwendung self als Vorname Parameter, dann get_class_from_frame wird falsch gib die Klasse des ersten Parameters zurück.
  • kann es bei der Arbeit mit Deskriptoren irreführend sein (es wird die Klasse des Deskriptors zurückgegeben, nicht die tatsächliche Instanz, auf die zugegriffen wird).
  • @classmethod und @staticmethod werden keine self Parameter und sind mit Deskriptoren implementiert.
  • und sicherlich viel mehr

Je nachdem, was genau wollen Sie tun, könnten Sie einige Zeit in Anspruch nehmen wollen, tiefer zu graben und Abhilfen für all diese Fragen finden (Sie könnten die Rahmenfunktion existieren überprüfen in der zurückgegebene Klasse und teilen die gleiche Quelle, Erkennung von Descriptoraufrufen ist möglich, gleiche für Klassenmethoden, etc ..)

+0

Vielen Dank für Ihre Antwort, Clément; es ist detailliert und durchdacht. Ich hoffe immer noch auf mehr als nur eine Antwort! Aber wenn nicht, muss dieser hier genügen ... –

+0

OK, warte lange genug. Das ist eine gute Antwort! Ich hatte auf bessere Unterstützung von den Rahmenstrukturen gehofft, aber es ist was es ist. Nochmals vielen Dank, Clément! –

+1

Um eins zur Liste hinzuzufügen, seien Sie vorsichtig, dass "self" eine Instanz einer abgeleiteten Klasse sein könnte, die möglicherweise nicht die ist, an der wir interessiert sind. – gertjan

7

Dies ist ein bisschen kürzer, aber etwa gleich. Gibt None zurück, wenn der Klassenname nicht verfügbar ist.

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

Schöne Antwort! Noch besser, du könntest '' f.f_locals ['__ class __'] .__ name__'', was auch '' classmethod'' und '' staticmethod'' Fälle abdecken sollte. –

+2

@LucioPaiva, die nicht funktioniert. In f_locals gibt es keinen Schlüssel "__class __". – Pithikos

2

Ich bin gerade auf diesen Beitrag gestoßen, da ich mit dem gleichen Problem konfrontiert war. Ich hielt die "Selbst" -Methode jedoch aus allen bereits genannten Gründen für keine akzeptable Lösung.

Der folgende Code demonstriert einen anderen Ansatz: Bei einem Rahmenobjekt durchsucht er die Globals nach einem Objekt mit übereinstimmendem Elementnamen und Codeblock. Die Suche ist nicht erschöpfend, daher ist es möglich, dass nicht alle Klassen aufgedeckt werden, aber welche Klassen gefunden werden, sollten diejenigen sein, nach denen wir suchen, weil wir übereinstimmende Codes verifizieren.

Objekt des Codes ist eine Funktion Namen mit seinen Klassennamen vorangestellt wird, wenn gefunden:

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 

Beachten Sie die Verwendung von __dict__ statt getattr zu verhindern abgeleiteten Klassen zu kontrollieren.

Hinweis weiter, dass eine globale Suche vermieden werden kann, wenn self = frame.f_locals['self']; obj = self.__class__ gibt eine Übereinstimmung, oder eine obj in self.__class__.__bases__ oder tiefer, so gibt es sicherlich Raum für die Optimierung/Hybridisierung.

1

Wenn eine Methode eine Klassenmethode ist, wird die Klasse das erste Argument sein. Dies gibt den Typ des ersten Arguments aus, wenn es für jeden aufrufenden Stapelrahmen vorhanden ist:

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__) 
Verwandte Themen