È possibile recuperare qualsiasi informazione di classe da un oggetto frame? So come ottenere il file (frame.f_code.co_filename), function (frame.f_code.co_name) e il numero di riga (frame.f_lineno), ma vorrei essere in grado di ottenere anche il nome della classe dell'oggetto attivo istanza del frame (o None se non in un'istanza).Python: come recuperare le informazioni sulla classe da un oggetto 'frame'?
risposta
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, quindiget_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 parametroself
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.)
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
Bella risposta! Ancora meglio, si potrebbe fare con '' f.f_locals ['__ class __'] .__ name__'', che dovrebbe coprire anche i casi '' @ classmethod'' e '' @ staticmethod''. –
@LucioPaiva che non funziona. Non esiste una chiave "__class __" in f_locals. – Pithikos
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.
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__)
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 ... –
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! –
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