Ce fil explique comment obtenir le nom d'une fonction sous forme de chaîne en Python: Comment obtenir un nom de fonction sous forme de chaîne en Python?
Comment puis-je faire la même chose pour une variable? (notez que les variables Python ne possèdent pas l'attribut __name__
, du moins dans Python 2.7.x
)
En d'autres termes, si j'ai une variable telle que:
foo = dict()
foo['bar'] = 2
Je recherche une fonction/un attribut, par exemple retrieve_name
:
retrieve_name(foo)
qui retourne la chaîne 'foo'
Puisque les gens me demandent pourquoi je veux faire cela, voici un exemple. Je voudrais créer un DataFrame dans les pandas à partir de cette liste , où les noms de colonnes sont donnés par les noms des dictionnaires actuels:
# List of dictionaries for my DataFrame
list_of_dicts = [n_jobs, users, queues, priorities]
Les seuls objets de Python ayant des noms canoniques sont les modules, les fonctions et les classes, et bien entendu, rien ne garantit que ce nom canonique ait une signification dans un espace de nommage après la définition de la fonction ou de la classe ou l'importation du module. Ces noms peuvent également être modifiés après la création des objets afin de ne pas toujours être particulièrement dignes de confiance.
Ce que vous voulez faire n’est pas possible sans parcourir récursivement l’arbre des objets nommés ; un nom est une référence à sens unique à un objet. Un objet Python commun ou une variété jardinière ne contient aucune référence à ses noms. Imaginez si chaque entier, chaque dictée, chaque liste, chaque booléen devait conserver une liste de chaînes représentant les noms qui en faisaient référence! Ce serait un cauchemar de mise en œuvre, avec peu d'avantages pour le programmeur.
Même si les valeurs de variable ne renvoient pas au nom, vous avez accès à la liste de chaque variable affectée et à sa valeur. Je suis donc étonné qu'une seule personne ait suggéré de s'y déplacer pour rechercher votre nom de variable.
Quelqu'un a mentionné dans cette réponse qu'il serait peut-être nécessaire de parcourir la pile et de rechercher les variables locales et globales pour trouver foo
, mais si foo
est affecté dans l'étendue où vous appelez cette fonction retrieve_name
, vous pouvez utiliser le current frame
de inspect
ces variables locales. Mon explication est peut-être un peu trop verbeuse (j'aurais peut-être dû utiliser un "moins" de mots), mais voici à quoi cela ressemblerait dans le code ( Notez que s'il existe plus d'une variable affectée à la même valeur, vous obtiendrez ces deux noms de variable ):
import inspect
x,y,z = 1,2,3
def get_var_name(var):
callers_local_vars = inspect.currentframe().f_back.f_locals.items()
return [k for k, v in callers_local_vars if v is var]
print(get_var_name(y))
Je ne crois pas que ce soit possible. Prenons l'exemple suivant:
>>> a = []
>>> b = a
>>> id(a)
140031712435664
>>> id(b)
140031712435664
Les a
et b
pointent sur le même objet, mais celui-ci ne peut pas savoir quelles variables pointent vers lui.
def name(**variables):
return [x for x in variables]
C'est utilisé comme ça:
name(variable=variable)
Sur python3, cette fonction obtiendra le nom le plus externe dans la pile:
import inspect
def retrieve_name(var):
"""
Gets the name of var. Does it from the out most frame inner-wards.
:param var: variable to get name from.
:return: string
"""
for fi in reversed(inspect.stack()):
names = [var_name for var_name, var_val in fi.frame.f_locals.items() if var_val is var]
if len(names) > 0:
return names[0]
C'est utile n'importe où sur le code. Parcourt la pile inversée à la recherche du premier match.
Voici une approche. Je ne recommanderais pas ceci pour quelque chose d'important, parce que ce sera assez fragile. Mais cela peut être fait.
Créez une fonction qui utilise le module inspect
pour rechercher le code source qui l'a appelé. Ensuite, vous pouvez analyser le code source pour identifier les noms de variables que vous souhaitez récupérer. Par exemple, voici une fonction appelée autodict
qui prend une liste de variables et retourne un dictionnaire mappant les noms de variables sur leurs valeurs. Par exemple.:
x = 'foo'
y = 'bar'
d = autodict(x, y)
print d
Donnerait:
{'x': 'foo', 'y': 'bar'}
Il est préférable d’inspecter le code source lui-même que de rechercher dans locals()
ou globals()
car cette dernière approche ne vous indique pas quelles des variables sont celles que vous voulez.
En tout cas, voici le code:
def autodict(*args):
get_rid_of = ['autodict(', ',', ')', '\n']
calling_code = inspect.getouterframes(inspect.currentframe())[1][4][0]
calling_code = calling_code[calling_code.index('autodict'):]
for garbage in get_rid_of:
calling_code = calling_code.replace(garbage, '')
var_names, var_values = calling_code.split(), args
dyn_dict = {var_name: var_value for var_name, var_value in
Zip(var_names, var_values)}
return dyn_dict
L'action se produit dans la ligne avec inspect.getouterframes
, qui renvoie la chaîne dans le code appelé autodict
.
L'inconvénient évident de ce type de magie est qu'il suppose des hypothèses sur la structure du code source. Et bien sûr, cela ne fonctionnera pas du tout si cela se passe à l'intérieur de l'interprète.
J'ai écrit le paquet sorcellerie pour faire ce genre de magie avec robustesse. Tu peux écrire:
from sorcery import dict_of
columns = dict_of(n_jobs, users, queues, priorities)
et transmettez cela au constructeur de la structure de données. C'est équivalent à:
columns = dict(n_jobs=n_jobs, users=users, queues=queues, priorities=priorities)
En Python, les mots clés def
et class
lieront un nom spécifique à l'objet qu'ils définissent (fonction ou classe). De même, les modules reçoivent un nom car ils sont appelés quelque chose de spécifique dans le système de fichiers. Dans les trois cas, il existe un moyen évident d'attribuer un nom "canonique" à l'objet en question.
Toutefois, pour d’autres types d’objets, un tel nom canonique peut être _ {n’existe tout simplement pas} _. Par exemple, considérons les éléments d'une liste. Les éléments de la liste ne sont pas nommés individuellement et il est tout à fait possible que le seul moyen de s'y référer dans un programme consiste à utiliser des index de liste dans la liste contenue. Si une telle liste d'objets était transmise à votre fonction, vous ne pourriez probablement pas attribuer d'identificateurs significatifs aux valeurs.
Python n'enregistre pas le nom situé à gauche d'une affectation dans l'objet affecté, car:
Ainsi, par exemple, les fonctions définies à l'aide de lambda
auront toujours le "nom" <lambda>
, plutôt qu'un nom de fonction spécifique.
La meilleure approche consisterait simplement à demander à l'appelant de fournir une liste (facultative) de noms. Si la saisie du '...','...'
est trop lourde, vous pouvez accepter par exemple une seule chaîne contenant une liste de noms séparés par des virgules (comme le fait namedtuple
).
Je pense que c'est si difficile de faire cela en Python à cause du simple fait que vous ne saurez jamais le nom de la variable que vous utilisez. Donc, dans son exemple, vous pourriez faire:
Au lieu de:
list_of_dicts = [n_jobs, users, queues, priorities]
dict_of_dicts = {"n_jobs" : n_jobs, "users" : users, "queues" : queues, "priorities" : priorities}
>>> locals()['foo']
{}
>>> globals()['foo']
{}
si vous voulez écrire votre propre fonction, vous pouvez faire en sorte de rechercher une variable définie dans les paramètres locaux, puis de vérifier les variables globales. Si rien n'est trouvé, vous pouvez comparer sur id () pour voir si la variable pointe au même endroit en mémoire.
Si votre variable est dans une classe, vous pouvez utiliser className. dict . Keys () ou vars (self) pour voir si votre variable a été définie.
Cette fonction affichera le nom de la variable avec sa valeur:
def print_this(var):
callers_local_vars = inspect.currentframe().f_back.f_locals.items()
print(str([k for k, v in callers_local_vars if v is var][0])+': '+str(var))
***Input & Function call:*** my_var = 10 print_this(my_var) ***Output**:* my_var: 10
Vous pouvez essayer ce qui suit pour récupérer le nom d'une fonction que vous avez définie (ne fonctionne pas pour les fonctions intégrées cependant):
import re
def retrieve_name(func):
return re.match("<function\s+(\w+)\s+at.*", str(func)).group(1)
def foo(x):
return x**2
print(retrieve_name(foo))
# foo
J'essaie d'obtenir le nom des sections locales de l'inspection, mais il ne peut pas traiter var aime un [1], b.val . ça succ! code comme ci-dessous:
#direct get from called function code
def retrieve_name_ex(var):
stacks = inspect.stack()
try:
func = stacks[0].function
code = stacks[1].code_context[0]
s = code.index(func)
s = code.index("(", s + len(func)) + 1
e = code.index(")", s)
return code[s:e].strip()
except:
return ""
Pour les constantes, vous pouvez utiliser une énumération, qui permet d'extraire son nom.
Si l'objectif est de vous aider à garder une trace de vos variables, vous pouvez écrire une fonction simple qui étiquette la variable et renvoie sa valeur et son type. Par exemple, supposons que i_f = 3.01 et que vous l'entouriez d'un nombre entier appelé i_n à utiliser dans un code, puis que vous ayez besoin d'une chaîne i_s qui sera insérée dans un rapport.
def whatis(string, x):
print(string+' value=',repr(x),type(x))
return string+' value='+repr(x)+repr(type(x))
i_f=3.01
i_n=int(i_f)
i_s=str(i_n)
i_l=[i_f, i_n, i_s]
i_u=(i_f, i_n, i_s)
## make report that identifies all types
report='\n'+20*'#'+'\nThis is the report:\n'
report+= whatis('i_f ',i_f)+'\n'
report+=whatis('i_n ',i_n)+'\n'
report+=whatis('i_s ',i_s)+'\n'
report+=whatis('i_l ',i_l)+'\n'
report+=whatis('i_u ',i_u)+'\n'
print(report)
Ceci s’imprime dans la fenêtre à chaque appel à des fins de débogage et génère également une chaîne pour le rapport écrit. Le seul inconvénient est que vous devez taper la variable deux fois à chaque fois que vous appelez la fonction.
Je suis un débutant en Python et j'ai trouvé ce moyen très utile de consigner mes efforts lorsque je programme et tente de gérer tous les objets en Python. Whatis () échoue s’il appelle une fonction décrite en dehors de la procédure où il est utilisé. Par exemple, int (i_f) était un appel de fonction valide uniquement parce que la fonction int est connue de Python. Vous pouvez appeler whatis () en utilisant int (i_f ** 2), mais si pour une raison étrange, vous choisissez de définir une fonction appelée int_squared, elle doit être déclarée dans la procédure où whatis () est utilisé.
juste une autre façon de le faire en fonction du contenu de la variable d'entrée:
(il renvoie le nom de la première variable qui correspond à la variable d'entrée, sinon, aucune. On peut la modifier pour obtenir tous les noms de variables ayant le même contenu que la variable d'entrée)
def retrieve_name(x, Vars=vars()):
for k in Vars:
if type(x) == type(Vars[k]):
if x is Vars[k]:
return k
return None