2010-02-04 5 views

risposta

16

Non credo che, a livello di oggetto frame, ci sia un modo per trovare l'oggetto della funzione Python che è stato chiamato.

Tuttavia, se il codice si basano sulla convenzione comune: denominazione del parametro di un metodo self esempio, allora si potrebbe procedere come segue:

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 

Se non si desidera utilizzare getargvalues, è possibile utilizzare direttamente frame.f_locals anziché value_dict e frame.f_code.co_varnames[:frame.f_code.co_argcount] anziché args.

tenere presente che questo è ancora solo affidamento sulla convenzione, quindi non è portatile, e soggetto ad errori:

  • se una funzione uso non metodo self come primo nome di parametro, quindi get_class_from_frame indebitamente sarà restituisce la classe del primo parametro.
  • può essere fuorviante quando si lavora con descrittori (restituirà la classe del descrittore, non dell'istanza effettiva a cui si accede).
  • @classmethod e @staticmethod non avranno un parametro self e sono implementati con descrittori.
  • e sicuramente molto più

A seconda di cosa esattamente si vuole fare, si potrebbe desiderare di prendere un po 'di tempo per scavare più a fondo e trovare soluzioni alternative per tutti questi problemi (si potrebbe verificare la funzione di telaio esistere nel restituisce classe e condivide la stessa fonte, è possibile rilevare le chiamate dei descrittori, lo stesso per i metodi di classe, ecc.)

+0

Grazie per la risposta, Clément; è dettagliato e premuroso. Sto ancora sperando di ottenere più di una sola risposta! Ma se no, questo dovrà essere sufficiente ... –

+0

OK, aspettato abbastanza a lungo. Questa è una buona risposta! Avevo sperato in un migliore supporto dalle strutture del telaio, ma è quello che è. Grazie ancora, Clément! –

+1

Per aggiungerne uno alla lista, fai attenzione che 'self' possa essere un'istanza di una classe derivata, che potrebbe non essere quella a cui siamo interessati. – gertjan

7

Questo è un po 'più breve, ma fa lo stesso. Restituisce Nessuno se il nome della classe non è disponibile.

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

Bella risposta! Ancora meglio, si potrebbe fare con '' f.f_locals ['__ class __'] .__ name__'', che dovrebbe coprire anche i casi '' @ classmethod'' e '' @ staticmethod''. –

+2

@LucioPaiva che non funziona. Non esiste una chiave "__class __" in f_locals. – Pithikos

2

Mi sono imbattuto in questo post perché mi trovavo di fronte allo stesso problema. Non ho considerato il metodo 'self' una soluzione accettabile, tuttavia, per tutti i motivi già elencati.

Il codice seguente dimostra un approccio diverso: dato un oggetto frame, cerca le globali per un oggetto con nome membro e blocco di codice corrispondenti. La ricerca è difficilmente esaustiva quindi è possibile che non tutte le classi vengano scoperte, ma quali classi vengano trovate dovrebbero essere quelle che stiamo cercando perché verifichiamo i codici corrispondenti.

Oggetto del codice è quello di anteporre un nome di funzione con il suo nome di classe, se trovato:

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 

Nota l'uso di __dict__ invece di getattr per evitare la cattura di classi derivate.

Nota inoltre che una ricerca globale può essere evitata se self = frame.f_locals['self']; obj = self.__class__ dà una corrispondenza, o qualsiasi obj in self.__class__.__bases__ o più profondo, quindi c'è sicuramente spazio per l'ottimizzazione/ibridazione.

1

Se un metodo è un metodo di classe, la classe sarà il primo argomento. Questo stampa il tipo del primo argomento se presente per ogni frame dello stack chiamante:

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__) 
Problemi correlati