J'aimerais pouvoir obtenir le nom d'une variable sous forme de chaîne mais je ne sais pas si Python possède autant de capacités d'introspection. Quelque chose comme:
>>> print(my_var.__name__)
'my_var'
Je veux faire cela parce que j'ai un tas de vars que je voudrais transformer en dictionnaire comme:
bar = True
foo = False
>>> my_dict = dict(bar=bar, foo=foo)
>>> print my_dict
{'foo': False, 'bar': True}
Mais j'aimerais quelque chose de plus automatique que ça.
Python a locals()
et vars()
, donc je suppose qu’il ya un moyen.
Est-ce que vous essayez de faire ça?
dict( (name,eval(name)) for name in ['some','list','of','vars'] )
Exemple
>>> some= 1
>>> list= 2
>>> of= 3
>>> vars= 4
>>> dict( (name,eval(name)) for name in ['some','list','of','vars'] )
{'list': 2, 'some': 1, 'vars': 4, 'of': 3}
Comme dit déroulant, ce n'est pas vraiment quelque chose que vous faites en Python - les variables sont en fait des correspondances de noms avec des objets.
Cependant , voici un moyen d'essayer de le faire:
>>> a = 1
>>> for k, v in list(locals().iteritems()):
if v is a:
a_as_str = k
>>> a_as_str
a
>>> type(a_as_str)
'str'
J'ai eu envie de faire ça beaucoup. Ce hack est très similaire à la suggestion de rlotun, mais c'est un one-liner, ce qui est important pour moi:
blah = 1
blah_name = [ k for k,v in locals().iteritems() if v is blah][0]
Python 3+
blah = 1
blah_name = [ k for k,v in locals().items() if v is blah][0]
C'est un hack. Cela ne fonctionnera pas sur toutes les distributions d'implémentations Python (en particulier celles qui n'ont pas traceback.extract_stack
.)
import traceback
def make_dict(*expr):
(filename,line_number,function_name,text)=traceback.extract_stack()[-2]
begin=text.find('make_dict(')+len('make_dict(')
end=text.find(')',begin)
text=[name.strip() for name in text[begin:end].split(',')]
return dict(Zip(text,expr))
bar=True
foo=False
print(make_dict(bar,foo))
# {'foo': False, 'bar': True}
Notez que ce hack est fragile:
make_dict(bar,
foo)
(appeler make_dict sur 2 lignes) ne fonctionnera pas.
Au lieu d'essayer de générer le dict à partir des values foo
et bar
, Il serait beaucoup plus pythonique de générer le dict à partir de la chaîne noms de variables 'foo'
et 'bar'
:
dict([(name,locals()[name]) for name in ('foo','bar')])
Ce n'est pas possible en Python, qui n'a pas vraiment de "variables". Python a des noms et il peut y avoir plusieurs noms pour le même objet.
Je pense que mon problème aidera à illustrer la raison pour laquelle cette question est utile et peut donner un peu plus de perspicacité pour savoir comment y répondre. J'ai écrit une petite fonction pour effectuer une vérification de la tête inline rapide sur diverses variables de mon code. Fondamentalement, il répertorie le nom de la variable, le type de données, la taille et d'autres attributs, afin que je puisse rapidement détecter les erreurs que j'ai commises. Le code est simple:
def details(val):
vn = val.__ # If such a thing existed
vs = str(val)
print("The Value of "+ str(vn) + " is " + vs)
print("The data type of " + vn + " is " + str(type(val)))
Donc, si vous avez une situation compliquée dictionnaire/liste/tuple, il serait très utile de demander à l'interprète de renvoyer le nom de variable que vous avez attribué. Par exemple, voici un dictionnaire étrange:
m = 'abracadabra'
mm=[]
for n in m:
mm.append(n)
mydic = {'first':(0,1,2,3,4,5,6),'second':mm,'third':np.arange(0.,10)}
details(mydic)
The Value of mydic is {'second': ['a', 'b', 'r', 'a', 'c', 'a', 'd', 'a', 'b', 'r', 'a'], 'third': array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.]), 'first': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
The data type of mydic is <type 'dict'>
details(mydic['first'])
The Value of mydic['first'] is (0, 1, 2, 3, 4, 5, 6)]
The data type of mydic['first'] is <type 'list'>
details(mydic.keys())
The Value of mydic.keys() is ['second', 'third', 'first']
The data type of mydic.keys() is <type 'Tuple'>
details(mydic['second'][0])
The Value of mydic['second'][0] is a
The data type of mydic['second'][0] is <type 'str'>
Je ne sais pas si je mets cela au bon endroit, mais je pensais que cela pourrait aider. J'espère que c'est le cas.
J'ai écrit une petite fonction utile utile basée sur la réponse à cette question. Je le mets ici au cas où cela serait utile.
def what(obj, callingLocals=locals()):
"""
quick function to print name of input and value.
If not for the default-Valued callingLocals, the function would always
get the name as "obj", which is not what I want.
"""
for k, v in list(callingLocals.items()):
if v is obj:
name = k
print(name, "=", obj)
usage:
>> a = 4
>> what(a)
a = 4
>>|
En python 3 c'est facile
myVariable = 5
for v in locals():
if id(v) == id("myVariable"):
print(v, locals()[v])
cela va imprimer:
myVariable 5
Je trouve que si vous avez déjà une liste de valeurs spécifique, cela est décrit par @S. Lotts est le meilleur; Cependant, la manière décrite ci-dessous fonctionne bien pour obtenir toutes les variables et les classes ajoutées dans le codeSANSla nécessité de fournir un nom de variable bien que vous puissiez les spécifier si vous le souhaitez. Le code peut être étendu pour exclure les classes.
import types
import math # mainly showing that you could import what you will before d
# Everything after this counts
d = dict(globals())
def kv_test(k,v):
return (k not in d and
k not in ['d','args'] and
type(v) is not types.FunctionType)
def magic_print(*args):
if len(args) == 0:
return {k:v for k,v in globals().iteritems() if kv_test(k,v)}
else:
return {k:v for k,v in magic_print().iteritems() if k in args}
if __== '__main__':
foo = 1
bar = 2
baz = 3
print magic_print()
print magic_print('foo')
print magic_print('foo','bar')
Sortie:
{'baz': 3, 'foo': 1, 'bar': 2}
{'foo': 1}
{'foo': 1, 'bar': 2}
Voici la fonction que j'ai créée pour lire les noms de variables. C'est plus général et peut être utilisé dans différentes applications:
def get_variable_name(*variable):
'''gets string of variable name
inputs
variable (str)
returns
string
'''
if len(variable) != 1:
raise Exception('len of variables inputed must be 1')
try:
return [k for k, v in locals().items() if v is variable[0]][0]
except:
return [k for k, v in globals().items() if v is variable[0]][0]
Pour l'utiliser dans la question spécifiée:
>>> foo = False
>>> bar = True
>>> my_dict = {get_variable_name(foo):foo,
get_variable_name(bar):bar}
>>> my_dict
{'bar': True, 'foo': False}
Python3. Utilisez inspect pour capturer l’espace de noms local appelant, puis utilisez les idées présentées ici. Peut renvoyer plus d'une réponse, comme cela a été souligné.
def varname(var):
import inspect
frame = inspect.currentframe()
var_id = id(var)
for name in frame.f_back.f_locals.keys():
try:
if id(eval(name)) == var_id:
return(name)
except:
pass
En lisant le fil, j'ai vu beaucoup de frictions. Il est assez facile de donner une mauvaise réponse, puis laissez quelqu'un donner la bonne réponse. Quoi qu'il en soit, voici ce que j'ai trouvé.
De: [effbot.org] ( http://effbot.org/zone/python-objects.htm#names )
Les noms sont un peu différents - ce ne sont pas vraiment des propriétés de l’objet, et l’objet lui-même ne sait pas comment il s’appelle.
Un objet peut avoir un nombre quelconque de noms ou aucun nom.
Les noms résident dans des espaces de noms (tels qu’un espace de noms de module, un espace de noms d’instance, un espace de noms local d’une fonction).
Note: ça dit l'objet lui-même ne sait pas comment il s'appelle, donc c'était la clé. Les objets Python ne sont pas auto-référentiels. Puis il dit, Les noms vivent dans des espaces de noms. Nous avons cela dans TCL/TK. Alors peut-être que ma réponse aidera (mais elle m'a aidé)
jj = 123 print eval ("'" + str (id (jj)) + "'")) print dir ()
166707048 ['__ builtins__', '__doc__', '__file__', '__name__', '__package__', 'jj']
Donc, il y a "jj" à la fin de la liste.
Réécrivez le code comme suit:
jj = 123 print eval ("'" + str (id (jj)) + "'") ] pour x dans dir (): print id (eval (x))
161922920 ['__ builtins__', '__doc__', '__file__', '__name__', '__package__', 'jj'] 3077447796 136515736 3077408320 3077656800 136515736 161922920
Ce code d'identification de code est le nom de variable/objet/tout-ce-que-vous-pedantics-call-it.
Tiens voilà. L'adresse mémoire de 'jj' est la même lorsque nous la recherchons directement, comme lorsque nous recherchons le dictionnaire dans un espace de noms global. Je suis sûr que vous pouvez créer une fonction pour le faire. Rappelez-vous simplement dans quel espace de nom se trouve votre variable/objet/wypci.
QED.
J'ai écrit le paquet sorcellerie pour faire ce genre de magie avec robustesse. Tu peux écrire:
from sorcery import dict_of
my_dict = dict_of(foo, bar)
import re
import traceback
pattren = re.compile(r'[\W+\w+]*get_variable_name\((\w+)\)')
def get_variable_name(x):
return pattren.match( traceback.extract_stack(limit=2)[0][3]) .group(1)
a = 1
b = a
c = b
print get_variable_name(a)
print get_variable_name(b)
print get_variable_name(c)
J'ai téléchargé une solution sur pypi . C'est un module définissant un équivalent de la fonction nameof
de C #.
Il parcourt les instructions en bytecode pour le cadre dans lequel il est appelé, en obtenant les noms des variables/attributs qui lui sont transmis. Les noms se trouvent dans les instructions .argrepr
of LOAD
après le nom de la fonction.
Python 2.7 et les versions plus récentes intègrent également un dictionnaire qui le rend un peu plus court. Si possible, j'utiliserais getattr à la place de eval (eval is evil), comme dans la réponse du haut. Self peut être n'importe quel objet qui a le contexte que vous regardez. Il peut s'agir d'un objet ou de locaux = locaux (), etc.
{name: getattr(self, name) for name in ['some', 'vars', 'here]}
La plupart des objets n'ont pas d'attribut_NAME_. (Les classes, les fonctions et les modules le font; y a-t-il d'autres types intégrés qui en ont un?)
Qu'attendez-vous d'autre pour print(my_var.__name__)
autre que print("my_var")
? Pouvez-vous simplement utiliser la chaîne directement?
Vous pouvez "trancher" un dict:
def dict_slice(D, keys, default=None):
return dict((k, D.get(k, default)) for k in keys)
print dict_slice(locals(), ["foo", "bar"])
# or use set literal syntax if you have a recent enough version:
print dict_slice(locals(), {"foo", "bar"})
Alternativement:
throw = object() # sentinel
def dict_slice(D, keys, default=throw):
def get(k):
v = D.get(k, throw)
if v is not throw:
return v
if default is throw:
raise KeyError(k)
return default
return dict((k, get(k)) for k in keys)
Eh bien, j’ai rencontré le même besoin il ya quelques jours et devait obtenir la variable name qui pointait vers le objet lui-même.
Et pourquoi était-ce si nécessaire?
En bref, je construisais un plug-in pour Maya. Le plug-in principal a été créé à l'aide de C++, mais l'interface graphique est dessinée via Python (car elle ne nécessite pas beaucoup de processeur). Depuis, je ne sais pas encore comment return
plusieurs valeurs du plug-in, à l'exception de la valeur par défaut MStatus
, donc pour mettre à jour un dictionnaire en Python, je devais passer le nom de la variable, en pointant sur l'objet implémentant l'interface graphique et qui contenait le dictionnaire lui-même, au plug-in, puis utilisait la fonction MGlobal::executePythonCommand()
pour mettre à jour le dictionnaire à partir de la portée globale de Maya.
Faire ce que j'ai fait était quelque chose comme:
import time
class foo(bar):
def __init__(self):
super(foo, self).__init__()
self.time = time.time() #almost guaranteed to be unique on a single computer
def name(self):
g = globals()
for x in g:
if isinstance(g[x], type(self)):
if g[x].time == self.time:
return x
#or you could:
#return filter(None,[x if g[x].time == self.time else None for x in g if isinstance(g[x], type(self))])
#and return all keys pointing to object itself
Je sais que ce n'est pas la solution parfaite dans la globals
beaucoup de clés pourraient pointer vers le même objet, par exemple:
a = foo()
b = a
b.name()
>>>b
or
>>>a
et que l'approche n'est pas thread-safe. Corrigez-moi si je me trompe.
Au moins, cette approche a résolu mon problème en récupérant le name de n’importe quelle variable de la portée globale qui pointait vers le object lui-même et en le passant au plug-in, en tant qu’argument, pour qu’il soit utilisé en interne.
J'ai essayé ceci sur int
(la classe entière primitive), mais le problème est que ces classes primitives ne sont pas ignorées (veuillez corriger la terminologie technique utilisée si elle est fausse). Vous pouvez ré-implémenter int
puis faire int = foo
mais a = 3
ne sera jamais un objet de foo
mais de la primitive. Pour surmonter cela, vous devez a = foo(3)
pour que a.name()
fonctionne.
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.
Je travaillais sur un problème similaire. @ S.Lott a déclaré "Si vous avez la liste des variables, quel est l'intérêt de" découvrir "leurs noms?" Et ma réponse est juste pour voir si cela peut être fait et si pour une raison quelconque vous voulez trier vos variables par type dans des listes. En tout état de cause, mes recherches ont abouti à ce sujet et ma solution est un peu étendue et repose sur la solution @rlotun. Un autre élément, @unutbu, a déclaré: "Cette idée a du mérite, mais notez que si deux noms de variable font référence à la même valeur (par exemple, True), un nom de variable non intentionnel peut être renvoyé." Dans cet exercice, c’était vrai, j’ai donc traité le problème en utilisant une compréhension de liste semblable à celle-ci pour chaque possibilité: isClass = [i for i in isClass if i != 'item']
. Sans cela, "item" apparaîtrait dans chaque liste.
__metaclass__ = type
from types import *
class Class_1: pass
class Class_2: pass
list_1 = [1, 2, 3]
list_2 = ['dog', 'cat', 'bird']
Tuple_1 = ('one', 'two', 'three')
Tuple_2 = (1000, 2000, 3000)
dict_1 = {'one': 1, 'two': 2, 'three': 3}
dict_2 = {'dog': 'collie', 'cat': 'calico', 'bird': 'robin'}
x = 23
y = 29
pie = 3.14159
eee = 2.71828
house = 'single story'
cabin = 'cozy'
isClass = []; isList = []; isTuple = []; isDict = []; isInt = []; isFloat = []; isString = []; other = []
mixedDataTypes = [Class_1, list_1, Tuple_1, dict_1, x, pie, house, Class_2, list_2, Tuple_2, dict_2, y, eee, cabin]
print '\nMIXED_DATA_TYPES total count:', len(mixedDataTypes)
for item in mixedDataTypes:
try:
# if isinstance(item, ClassType): # use this for old class types (before 3.0)
if isinstance(item, type):
for k, v in list(locals().iteritems()):
if v is item:
mapping_as_str = k
isClass.append(mapping_as_str)
isClass = [i for i in isClass if i != 'item']
Elif isinstance(item, ListType):
for k, v in list(locals().iteritems()):
if v is item:
mapping_as_str = k
isList.append(mapping_as_str)
isList = [i for i in isList if i != 'item']
Elif isinstance(item, TupleType):
for k, v in list(locals().iteritems()):
if v is item:
mapping_as_str = k
isTuple.append(mapping_as_str)
isTuple = [i for i in isTuple if i != 'item']
Elif isinstance(item, DictType):
for k, v in list(locals().iteritems()):
if v is item:
mapping_as_str = k
isDict.append(mapping_as_str)
isDict = [i for i in isDict if i != 'item']
Elif isinstance(item, IntType):
for k, v in list(locals().iteritems()):
if v is item:
mapping_as_str = k
isInt.append(mapping_as_str)
isInt = [i for i in isInt if i != 'item']
Elif isinstance(item, FloatType):
for k, v in list(locals().iteritems()):
if v is item:
mapping_as_str = k
isFloat.append(mapping_as_str)
isFloat = [i for i in isFloat if i != 'item']
Elif isinstance(item, StringType):
for k, v in list(locals().iteritems()):
if v is item:
mapping_as_str = k
isString.append(mapping_as_str)
isString = [i for i in isString if i != 'item']
else:
for k, v in list(locals().iteritems()):
if v is item:
mapping_as_str = k
other.append(mapping_as_str)
other = [i for i in other if i != 'item']
except (TypeError, AttributeError), e:
print e
print '\n isClass:', len(isClass), isClass
print ' isList:', len(isList), isList
print ' isTuple:', len(isTuple), isTuple
print ' isDict:', len(isDict), isDict
print ' isInt:', len(isInt), isInt
print ' isFloat:', len(isFloat), isFloat
print 'isString:', len(isString), isString
print ' other:', len(other), other
# my output and the output I wanted
'''
MIXED_DATA_TYPES total count: 14
isClass: 2 ['Class_1', 'Class_2']
isList: 2 ['list_1', 'list_2']
isTuple: 2 ['Tuple_1', 'Tuple_2']
isDict: 2 ['dict_1', 'dict_2']
isInt: 2 ['x', 'y']
isFloat: 2 ['pie', 'eee']
isString: 2 ['house', 'cabin']
other: 0 []
'''
Peut-être que je pense trop mais ..
str_l = next((k for k,v in locals().items() if id(l) == id(v)))
>>> bar = True
>>> foo = False
>>> my_dict=dict(bar=bar, foo=foo)
>>> next((k for k,v in locals().items() if id(bar) == id(v)))
'bar'
>>> next((k for k,v in locals().items() if id(foo) == id(v)))
'foo'
>>> next((k for k,v in locals().items() if id(my_dict) == id(v)))
'my_dict'
vous pouvez utiliser easydict
>>> from easydict import EasyDict as edict
>>> d = edict({'foo':3, 'bar':{'x':1, 'y':2}})
>>> d.foo
3
>>> d.bar.x
1
>>> d = edict(foo=3)
>>> d.foo
3
un autre exemple:
>>> d = EasyDict(log=False)
>>> d.debug = True
>>> d.items()
[('debug', True), ('log', False)]
Bien que ce soit probablement une idée affreuse, cela va dans le même sens que la réponse de rlotun, mais le résultat correct sera renvoyé plus souvent.
import inspect
def getVarName(getvar):
frame = inspect.currentframe()
callerLocals = frame.f_back.f_locals
for k, v in list(callerLocals.items()):
if v is getvar():
callerLocals.pop(k)
try:
getvar()
callerLocals[k] = v
except NameError:
callerLocals[k] = v
del frame
return k
del frame
Vous l'appelez comme ça:
bar = True
foo = False
bean = False
fooName = getVarName(lambda: foo)
print(fooName) # prints "foo"
devrait obtenir la liste puis retourner
def get_var_name(**kwargs):
"""get variable name
get_var_name(var = var)
Returns:
[str] -- var name
"""
return list(kwargs.keys())[0]