102 votes

Convertir le nom de la variable en chaîne ?

J'aimerais convertir un nom de variable python en son équivalent en chaîne de caractères, comme indiqué. Comment faire ?

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

1 votes

Pourriez-vous préciser ce que vous essayez de faire ? Il semble que vous demandiez comment résoudre un problème de la mauvaise manière. Il y a aussi une question similaire, mais à propos de Ruby stackoverflow.com/questions/58482/ruby-get-a-variables-name

1 votes

J'ai vérifié cette question car, en utilisant RPy2, je veux passer une variable Python avec un nom R éponyme. Un exemple inutile (mais proche de mon application) : def to_R( py_var ): r.assign( 'py_var', py_var ) pour assigner les données de (Py) py_var à l'environnement R et avoir le même nom. Ce serait utile pour moi de le faire à la place, r.assign( py_var.stringof, py_var ) a la D puisque le nom de la variable passée à ma fn to_r n'est pas nécessairement py_var .

4 votes

L'une des façons de l'utiliser est lorsqu'une déclaration Assert échoue. Vous pouvez fournir à l'utilisateur le nom exact de la variable en question tout en conservant votre code générique, par exemple AssertionError : 'something_else' must be a number greater than 5 (erreur d'affirmation : 'quelque chose_autre' doit être un nombre supérieur à 5). Vous pouvez alors tester chaque variable qui doit être supérieure à 5 et obtenir le nom de la variable dans le message AssertionError.

59voto

Petru Zaharia Points 194

TL;DR : Impossible. Voir la "conclusion" à la fin.


Il existe un scénario d'utilisation pour lequel vous pourriez en avoir besoin. Je n'insinue pas qu'il n'existe pas de meilleurs moyens d'atteindre 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 dans d'autres situations similaires.

Ce qu'il faudrait, c'est l'inverse de la eval() fonction :

get_indentifier_name_missing_function()

qui prendrait un nom d'identifiant ('variable', 'dictionnaire', etc.) comme argument, et renverrait une contenant le nom de l'identifiant.


Considérons la situation actuelle suivante :

random_function(argument_data)

Si l'on passe un nom d'identifiant ('fonction', 'variable', 'dictionnaire', etc) argument_data à un random_function() (un autre nom d'identifiant), on transmet 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>)

D'après ce que j'ai compris, seule l'adresse mémoire est transmise à la fonction :

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

Par conséquent, il faudrait passer une chaîne de caractères comme argument à la fonction random_function() pour que cette fonction ait le nom de l'identifiant de l'argument :

random_function('argument_data')

Dans la fonction random_function()

def random_function(first_argument):

on utilise la chaîne de caractères déjà fournie 'argument_data' à :

  1. sert de "nom d'identification" (pour l'affichage, l'enregistrement, le fractionnement des chaînes/concat, etc.)

  2. nourrir le eval() afin d'obtenir une référence à l'identifiant réel, et donc 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. Elle ne fonctionne que si le random_function() peut résoudre le problème de la 'argument_data' à un identifiant réel. Par exemple, si argument_data Le nom de l'identificateur est disponible dans la base de données random_function() de l'espace de noms.

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 sont les suivants :

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

Parce que argument_data n'est pas disponible dans la base de données random_function() ce qui aurait pour effet de créer un espace de noms :

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

Considérons maintenant l'utilisation hypothétique d'un get_indentifier_name_missing_function() qui se comporterait comme décrit ci-dessus.

Voici un code Python 3.0 factice : .

# 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 sont les suivants :

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'identificateurs "originaux" ( some_dictionary_ , some_other_dictionary_2 , some_other_dictionary_n ). Il ne verrait que les a_dictionary_object nom de l'identifiant.

Le résultat réel serait donc plutôt le suivant :

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.

Ainsi, l'inverse de la eval() ne sera pas très utile dans ce cas.


Actuellement, c'est ce qu'il faudrait faire :

# 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 des adresses mémoire comme arguments aux fonctions.
  • Les chaînes de caractères représentant le nom d'un identifiant ne peuvent être référencées à l'identifiant réel que par la fonction eval() si l'identifiant du nom est disponible dans l'espace de noms actuel.
  • Une hypothétique inversion de la eval() ne serait pas utile dans les cas où le nom de l'identificateur 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 réel (adresse mémoire)

Pour ce faire, il suffit de transmettre à la fois l'élément 'string' et eval('string') à la fonction appelée en même temps. Je pense qu'il s'agit de la manière la plus "générale" de résoudre ce problème de la poule aux œufs d'or pour des fonctions, des modules et des espaces de noms arbitraires, sans avoir recours à des solutions d'urgence. Le seul inconvénient est l'utilisation de la méthode eval() ce qui peut facilement conduire à un code non sécurisé. Il faut veiller à ne pas alimenter le eval() avec à peu près n'importe quoi, en particulier avec des données externes non filtrées.

34voto

Synthase Points 3646

Tout à fait possible avec le paquet python-varname (python3) :

from varname import nameof

s = 'Hey!'

print (nameof(s))

Salida:

s

Installer :

pip3 install varname

Vous pouvez également obtenir le paquet en cliquant ici :

https://github.com/pwwang/python-varname

14voto

Ken Manly Points 31

J'ai cherché cette question parce que je voulais 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 d'impression aurait besoin des noms de variables sous forme de chaînes de caractères. J'aurais pu fournir à mon code les chaînes de caractères "foo", "bar" et "baz", mais j'avais l'impression de me répéter. Après avoir lu les réponses précédentes, j'ai élaboré la solution ci-dessous.

La fonction globals() se comporte comme un dict avec des noms de variables (sous forme de chaînes) comme clés. Je voulais récupérer dans 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() recherche dans cette liste le(s) nom(s) de la variable qui correspond(ent) à la valeur de la variable dont j'ai besoin sous forme de chaîne de caractères.

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 la même que la valeur de la variable passée à variablename(). En fait, en utilisant la fonction is la fonction lambda teste si le nom de la variable testée est lié exactement au même objet que la variable passée à variablename(). Si c'est le cas, le tuple passe le test et est retourné 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 qu'elle soit appelée correctement, je l'ai placée à l'intérieur d'une compréhension de liste [tpl[0] for tpl ... globals().items())] . La compréhension de la liste n'enregistre que le nom de la 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 passée à variablename().

Dans les utilisations de variablename() présentées ci-dessous, la chaîne souhaitée est renvoyée sous la forme d'un élément d'une liste. Dans de nombreux cas, il s'agit du seul élément de la liste. Toutefois, si la même valeur est attribué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']

10voto

Alice Points 11

Tant qu'il s'agit d'une variable et non d'une seconde classe, cela fonctionne 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

et ceci pour les classes (à titre d'exemple) :

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

Ainsi, pour les classes, il vous donne le nom ET les propriétés.

0 votes

S'il vous plaît éditer votre message pour des questions de formatage. Il est difficile de comprendre ce que vous dites.

14 votes

C'est un réel plaisir de lire un billet qui répond à la question sans s'acharner sur la raison pour laquelle la question est posée.

2 votes

Notez qu'il est possible de lier la même valeur à plusieurs noms. Dans ce cas, le code ci-dessus imprimera plusieurs noms. Il n'y a aucun moyen de savoir quel est le "bon" nom.

7voto

steveha Points 24808

Ce n'est pas possible.

En Python, il n'existe pas vraiment de "variable". Ce que Python a vraiment, ce sont des "noms" auxquels peuvent être liés des objets. L'objet ne fait aucune différence quant aux noms auxquels il peut être lié, s'il y en a. Il peut être lié à des dizaines de noms différents, ou à aucun. Il peut être lié à des dizaines de noms différents, ou à aucun.

Prenons l'exemple suivant :

foo = 1
bar = 1
baz = 1

Supposons maintenant que vous disposiez de l'objet entier de valeur 1 et que vous souhaitiez remonter le temps pour trouver son nom. Qu'allez-vous imprimer ? Trois noms différents sont liés à cet objet, 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 existe peut-être un moyen astucieux de pirater les bytecodes Python pour obtenir la valeur du nom, mais il s'agit au mieux d'un tour de passe-passe.

Si vous savez que vous voulez print foo pour imprimer "foo" vous pouvez tout aussi bien vous contenter d'exécuter print "foo" en premier lieu.

EDIT : J'ai légèrement modifié la formulation pour la rendre plus claire. Voici également un exemple encore plus parlant :

foo = 1
bar = foo
baz = foo

En pratique, Python réutilise le même objet pour les entiers ayant des valeurs communes comme 0 ou 1, de sorte que le premier exemple devrait lier le même objet aux trois noms. Mais cet exemple est clair comme de l'eau de roche : le même objet est lié à foo, bar et baz.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X