web-dev-qa-db-fra.com

Un moyen plus simple de créer un dictionnaire de variables séparées?

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.

213
e-satis

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}
46
S.Lott

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'
130
rlotun

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]
61
keflavich

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')])
17
unutbu

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.

14
unwind

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.

10
TheProletariat

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
>>|
8
aquirdturtle

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

6
officialhopsof

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}
6
cdhagmann

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}
5
Anna

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
5
vvxxii

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.

4
Jesse Monroy Jr.

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)
2
Alex Hall
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)
2
xmduhan

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.

2
user7340830

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]}
1
yvess

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)
1
Roger Pate

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.

1
Bleeding Fingers

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.

1
juan Isaza

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 []
'''
1
Michael Swartz

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'
1
rh0dium

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)]
1
Eli

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"
0
Phylliida

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]
0
paulg