web-dev-qa-db-fra.com

Convertir un nom de variable en chaîne?

Je voudrais convertir un nom de variable python en équivalent de chaîne, comme indiqué. Des idées comment?

var = {}
print ???  # Would like to see 'var'
something_else = 3
print ???  # Would print 'something_else'
46
Brandon Pelfrey

Il existe un scénario d'utilisation dans lequel vous pourriez en avoir besoin. Je ne sous-entends pas qu'il n'y a pas de meilleurs moyens ou d'obtenir la même fonctionnalité.

Cela serait utile pour "vider" une liste arbitraire de dictionnaires en cas d'erreur, dans les modes de débogage et autres situations similaires.

Ce qui serait nécessaire est l’inverse de la fonction eval():

get_indentifier_name_missing_function()

qui prendrait un nom d’identifiant ('variable', 'dictionnaire', etc.) en tant qu’argument et renverrait une chaîne contenant le nom de l’identifiant.


Considérez la situation actuelle suivante:

random_function(argument_data)

Si l'on passe un nom d'identifiant ('fonction', 'variable', 'dictionnaire', etc.) argument_data à random_function() (autre nom d'identifiant), on passe en fait un identifiant (par exemple: <argument_data object at 0xb1ce10>) à un autre identifiant (par exemple: <function random_function at 0xafff78>) :

<function random_function at 0xafff78>(<argument_data object at 0xb1ce10>)

De ma compréhension, seule l'adresse mémoire est transmise à la fonction:

<function at 0xafff78>(<object at 0xb1ce10>)

Par conséquent, il faudrait passer une chaîne en tant qu'argument à random_function() pour que cette fonction ait le nom d'identifiant de l'argument:

random_function('argument_data')

Dans la random_function ()

def random_function(first_argument):

, on utiliserait la chaîne déjà fournie 'argument_data' pour:

  1. sert de 'nom d'identifiant' (pour afficher, consigner, diviser/concaténer une chaîne, peu importe)
  2. alimentez la fonction eval() afin d'obtenir une référence à l'identificateur actuel et, par conséquent, une référence aux données réelles:

    print("Currently working on", first_argument)
    some_internal_var = eval(first_argument)
    print("here comes the data: " + str(some_internal_var))
    

Malheureusement, cela ne fonctionne pas dans tous les cas. Cela ne fonctionne que si random_function() peut résoudre la chaîne 'argument_data' en un identificateur réel. C'est à dire. Si argument_data nom d'identifiant est disponible dans l'espace de noms de random_function().

Ce n'est pas toujours le cas:

# main1.py
import some_module1

argument_data = 'my data'

some_module1.random_function('argument_data')


# some_module1.py
def random_function(first_argument):
    print("Currently working on", first_argument)
    some_internal_var = eval(first_argument)
    print("here comes the data: " + str(some_internal_var))
######

Les résultats attendus seraient:

Currently working on: argument_data
here comes the data: my data

Étant donné que le nom d'identificateur argument_data n'est pas disponible dans l'espace de noms de random_function(), cela produirait à la place:

Currently working on argument_data
Traceback (most recent call last):
  File "~/main1.py", line 6, in <module>
    some_module1.random_function('argument_data')
  File "~/some_module1.py", line 4, in random_function
    some_internal_var = eval(first_argument)
  File "<string>", line 1, in <module>
NameError: name 'argument_data' is not defined

Maintenant, considérons l’utilisation hypotétique d’une get_indentifier_name_missing_function() qui se comporterait comme décrit ci-dessus.

Voici un code factice pour Python 3.0:.

# main2.py
import some_module2
some_dictionary_1       = { 'definition_1':'text_1',
                            'definition_2':'text_2',
                            'etc':'etc.' }
some_other_dictionary_2 = { 'key_3':'value_3',
                            'key_4':'value_4', 
                            'etc':'etc.' }
#
# more such stuff
#
some_other_dictionary_n = { 'random_n':'random_n',
                            'etc':'etc.' }

for each_one_of_my_dictionaries in ( some_dictionary_1,
                                     some_other_dictionary_2,
                                     ...,
                                     some_other_dictionary_n ):
    some_module2.some_function(each_one_of_my_dictionaries)


# some_module2.py
def some_function(a_dictionary_object):
    for _key, _value in a_dictionary_object.items():
        print( get_indentifier_name_missing_function(a_dictionary_object)    +
               "    " +
               str(_key) +
               "  =  " +
               str(_value) )
######

Les résultats attendus seraient:

some_dictionary_1    definition_1  =  text_1
some_dictionary_1    definition_2  =  text_2
some_dictionary_1    etc  =  etc.
some_other_dictionary_2    key_3  =  value_3
some_other_dictionary_2    key_4  =  value_4
some_other_dictionary_2    etc  =  etc.
......
......
......
some_other_dictionary_n    random_n  =  random_n
some_other_dictionary_n    etc  =  etc.

Malheureusement, get_indentifier_name_missing_function() ne verrait pas les noms d'identifiant 'originaux' (some_dictionary_, some_other_dictionary_2, some_other_dictionary_n). Il ne verrait que le nom d'identifiant a_dictionary_object.

Par conséquent, le résultat réel serait plutôt:

a_dictionary_object    definition_1  =  text_1
a_dictionary_object    definition_2  =  text_2
a_dictionary_object    etc  =  etc.
a_dictionary_object    key_3  =  value_3
a_dictionary_object    key_4  =  value_4
a_dictionary_object    etc  =  etc.
......
......
......
a_dictionary_object    random_n  =  random_n
a_dictionary_object    etc  =  etc.

Donc, l'inverse de la fonction eval() ne sera pas utile dans ce cas.


Actuellement, il faudrait faire ceci:

# main2.py same as above, except:

    for each_one_of_my_dictionaries_names in ( 'some_dictionary_1',
                                               'some_other_dictionary_2',
                                               '...',
                                               'some_other_dictionary_n' ):
        some_module2.some_function( { each_one_of_my_dictionaries_names :
                                     eval(each_one_of_my_dictionaries_names) } )


    # some_module2.py
    def some_function(a_dictionary_name_object_container):
        for _dictionary_name, _dictionary_object in a_dictionary_name_object_container.items():
            for _key, _value in _dictionary_object.items():
                print( str(_dictionary_name) +
                       "    " +
                       str(_key) +
                       "  =  " +
                       str(_value) )
    ######

En conclusion:

  • Python ne transmet que les adresses mémoire en tant qu'arguments aux fonctions.
  • Les chaînes représentant le nom d'un identifiant ne peuvent être référencées à l'identifiant réel par la fonction eval() que si l'identifiant du nom est disponible dans l'espace de noms actuel.
  • Une hypothétique inversion de la fonction eval() ne serait pas utile dans les cas où le nom de l'identifiant n'est pas "vu" directement par le code appelant. Par exemple. à l'intérieur de toute fonction appelée.
  • Actuellement, il faut passer à une fonction:
    1. la chaîne représentant le nom de l'identifiant
    2. l'identifiant actuel (adresse mémoire)

Cela peut être réalisé en transmettant les 'string' et eval('string') à la fonction appelée en même temps. Je pense que c’est la manière la plus générale de résoudre ce problème Egg-chicken à travers des fonctions, modules, espaces de noms arbitraires, sans utiliser de solution de casse à l’angle. Le seul inconvénient est l'utilisation de la fonction eval() qui peut facilement conduire à un code non sécurisé. Il faut veiller à ne pas alimenter la fonction eval() avec à peu près tout, en particulier des données d’entrée externe non filtrées.

35
Petru Zaharia

J'ai cherché cette question parce que je souhaitais qu'un programme Python imprime des instructions d'affectation pour certaines des variables du programme. Par exemple, il pourrait afficher "foo = 3, bar = 21, baz = 432". La fonction print aurait besoin des noms de variable sous forme de chaîne. J'aurais pu fournir mon code avec les chaînes "foo", "bar" et "baz", mais cela ressemblait à me répéter. Après avoir lu les réponses précédentes, j'ai développé la solution ci-dessous.

La fonction globals () se comporte comme un dict avec des noms de variables (sous la forme de chaînes) comme clés. Je voulais extraire de globals () la clé correspondant à la valeur de chaque variable. La méthode globals (). Items () renvoie une liste de tuples; dans chaque Tuple, le premier élément est le nom de la variable (sous forme de chaîne) et le second est la valeur de la variable. Ma fonction variablename () cherche dans cette liste le (s) nom (s) de variable correspondant à la valeur de la variable dont j'ai besoin du nom sous forme de chaîne.

La fonction itertools.ifilter () effectue la recherche en testant chaque tuple de la liste globals (). Items () avec la fonction lambda x: var is globals()[x[0]]. Dans cette fonction, x est le tuple testé; x [0] est le nom de la variable (sous forme de chaîne) et x [1] est la valeur. La fonction lambda teste si la valeur de la variable testée est identique à la valeur de la variable transmise à variablename (). En fait, en utilisant l'opérateur is, la fonction lambda teste si le nom de la variable testée est lié au même objet que la variable transmise à variablename (). Si c'est le cas, le tuple réussit le test et est renvoyé par ifilter ().

La fonction itertools.ifilter () renvoie en fait un itérateur qui ne renvoie aucun résultat tant qu'il n'est pas appelé correctement. Pour l'appeler correctement, je l'insère dans une liste de compréhension [tpl[0] for tpl ... globals().items())]. La compréhension de la liste enregistre uniquement le nom de variable tpl[0], en ignorant la valeur de la variable. La liste créée contient un ou plusieurs noms (sous forme de chaînes) liés à la valeur de la variable transmise à variablename ().

Dans les utilisations de variablename () ci-dessous, la chaîne souhaitée est renvoyée sous forme d'élément dans une liste. Dans de nombreux cas, ce sera le seul élément de la liste. Si la même valeur est affectée à un autre nom de variable, la liste sera plus longue.

>>> def variablename(var):
...     import itertools
...     return [tpl[0] for tpl in 
...     itertools.ifilter(lambda x: var is x[1], globals().items())]
... 
>>> var = {}
>>> variablename(var)
['var']
>>> something_else = 3
>>> variablename(something_else)
['something_else']
>>> yet_another = 3
>>> variablename(something_else)
['yet_another', 'something_else']
8
Ken Manly

Ce n'est pas possible.

En Python, il n’existe pas vraiment de "variable". Ce que Python a vraiment, ce sont des "noms" auxquels des objets peuvent être liés. Cela ne fait aucune différence avec les noms d'objet auxquels il pourrait être lié, le cas échéant. Il pourrait être lié à des dizaines de noms différents, voire aucun.

Considérons cet exemple:

foo = 1
bar = 1
baz = 1

Supposons maintenant que l'objet entier ait la valeur 1 et que vous souhaitiez travailler en arrière et trouver son nom. Que voudriez-vous imprimer? Cet objet est lié à trois noms différents et tous sont également valides.

En Python, un nom est un moyen d'accéder à un objet, il n'y a donc aucun moyen de travailler directement avec les noms. Il pourrait y avoir un moyen astucieux de pirater les bytecodes de Python ou quelque chose pour obtenir la valeur du nom, mais c'est au mieux un tour de passe-passe.

Si vous savez que vous voulez que print foo imprime "foo", vous pouvez aussi simplement exécuter print "foo" en premier lieu.

EDIT: J'ai légèrement modifié le libellé pour le rendre plus clair. En outre, voici un exemple encore meilleur:

foo = 1
bar = foo
baz = foo

En pratique, Python réutilise le même objet pour les entiers ayant des valeurs communes telles que 0 ou 1, le premier exemple doit donc lier le même objet aux trois noms. Mais cet exemple est limpide: le même objet est lié à foo, bar et baz.

7
steveha

Techniquement, l’information vous est disponible, mais comme d’autres l’ont demandé, comment vous en serviriez-vous judicieusement?

>>> x = 52
>>> globals()
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', 
'x': 52, '__doc__': None, '__package__': None}

Cela montre que le nom de la variable est présent sous forme de chaîne dans le dictionnaire globals ().

>>> globals().keys()[2]
'x'

Dans ce cas, il s’agit de la troisième clé, mais il n’existe aucun moyen fiable de savoir où un nom de variable donné aboutira.

>>> for k in globals().keys():
...   if not k.startswith("_"):
...     print k
...
x
>>>

Vous pouvez filtrer les variables système de cette manière, mais vous obtiendrez tous vos propres éléments. Le simple fait d'exécuter ce code ci-dessus a créé une autre variable "k" qui a modifié la position de "x" dans le dict.

Mais c'est peut-être un début utile pour vous. Si vous nous dites à quoi vous voulez que cette capacité, des informations plus utiles pourraient éventuellement être données.

7
Todd

tant qu'il s'agit d'une variable et non d'une seconde classe, cela fonctionne ici pour moi:

def print_var_name(variable):
 for name in globals():
     if eval(name) == variable:
        print name
foo = 123
print_var_name(foo)
>>>foo

cela se produit pour les membres de la classe:

class xyz:
     def __init__(self):
         pass
member = xyz()
print_var_name(member)
>>>member

ans this pour les classes (à titre d'exemple):

abc = xyz
print_var_name(abc)
>>>abc
>>>xyz

Donc, pour les cours, il vous donne le nom ET les propriétés

4
Alice

Qu'essayez-vous de réaliser? Il n’ya absolument aucune raison de faire ce que vous décrivez et il existe probablement une bien meilleure solution au problème que vous essayez de résoudre.

L'alternative la plus évidente à ce que vous demandez est un dictionnaire. Par exemple:

>>> my_data = {'var': 'something'}
>>> my_data['something_else'] = 'something'
>>> print my_data.keys()
['var', 'something_else']
>>> print my_data['var']
something

Principalement sous forme de .. défi, j'ai implémenté la sortie souhaitée. N'utilisez pas ce code, s'il vous plaît!

#!/usr/bin/env python2.6
class NewLocals:
    """Please don't ever use this code.."""
    def __init__(self, initial_locals):
        self.prev_locals = list(initial_locals.keys())

    def show_new(self, new_locals):
        output = ", ".join(list(set(new_locals) - set(self.prev_locals)))
        self.prev_locals = list(new_locals.keys())
        return output
# Set up
eww = None
eww = NewLocals(locals())

# "Working" requested code

var = {}

print eww.show_new(locals())  # Outputs: var

something_else = 3
print eww.show_new(locals()) # Outputs: something_else

# Further testing

another_variable = 4
and_a_final_one = 5

print eww.show_new(locals()) # Outputs: another_variable, and_a_final_one
3
dbr

Vous devez en quelque sorte faire référence à la variable dont vous souhaitez imprimer le nom. Donc, cela ressemblerait à:

print varname(something_else)

Une telle fonction n’existe pas, mais s’il en existait une, ce serait inutile. Vous devez taper something_else afin de pouvoir taper des guillemets à gauche et à droite de celui-ci pour afficher le nom sous forme de chaîne:

print "something_else"
3
sth

Voici une variante succincte qui vous permet de spécifier n'importe quel répertoire ..__ Le problème avec l'utilisation de répertoires pour rechercher n'importe quoi est que plusieurs variables peuvent avoir la même valeur. Donc, ce code retourne une liste de variables possibles.

def varname( var, dir=locals()):
  return [ key for key, val in dir.items() if id( val) == id( var)]
2
ZevGriner

Je pense que c'est une solution intéressante et je suppose que vous pouvez obtenir le meilleur. Mais voyez-vous un moyen de gérer les résultats ambigus, votre fonction peut renvoyer? Comme l'opérateur "est" se comporte de manière inattendue avec des entiers variablename-function pourrait donner des résultats ambigus avec une probabilité élevée. Dans mon cas, je voudrais créer un décorateur, qui ajoute une nouvelle variable à une classe par le varialbename que je lui transmets:

def inject(klass, dependency):
klass.__dict__["__"+variablename(dependency)]=dependency

Mais si votre méthode renvoie des résultats ambigus, comment puis-je connaître le nom de la variable que j'ai ajoutée?

var any_var="myvarcontent"
var myvar="myvarcontent"
@inject(myvar)
class myclasss():
    def myclass_method(self):
        print self.__myvar    #I can not be sure, that this variable will be set...

Peut-être que si je vérifierais également la liste locale, je pourrais au moins supprimer la variable "dépendance" de la liste, mais ce ne sera pas un résultat fiable.

2
fonzy

Django ne le fait-il pas lors de la génération de noms de champs?

http://docs.djangoproject.com/fr/dev//topics/db/models/#verbose-field-names

Cela me semble raisonnable.

2
Joe

Cela fonctionnera pour des types de données simples (str, int, float, list, etc.)

 >>> def my_print (var_str): 
 print var_str + ':', globals () [var_str] 
 >>> a = 5 
 >>> b = ['hello', ', world!'] 
 >>> my_print ('a ') 
 a: 5 
 >>> my_print (' b ') 
 b: [' bonjour ',', monde! '] 
0
Hadsbjerg