web-dev-qa-db-fra.com

Puis-je imprimer le nom de la variable d'origine en Python?

J'ai enum et utilise les variables comme myEnum.SomeNameA, myEnum.SomeNameB, etc. Lorsque je renvoie une de ces variables à partir d'une fonction, puis-je imprimer leurs noms (tels que myEnum.SomeNameA) au lieu de la valeur renvoyée?

51
user34537

Réponse courte: non.

Réponse longue: cela est possible avec certains hacks laids utilisant traceback, inspect et autres, mais ce n'est généralement pas recommandé pour le code de production. Voir par exemple:

Vous pouvez peut-être utiliser une solution de contournement pour traduire la valeur en un nom/chaîne de représentation. Si vous postez d'autres exemples de code et des détails sur ce que vous voulez, nous pouvons peut-être vous fournir une assistance plus approfondie.

32
Jay

Il n'existe pas de nom de variable unique ou original http://www.amk.ca/quotations/python-quotes/page-8

De la même manière que vous obtenez le nom de ce chat que vous avez trouvé sur votre véranda: le chat (objet) lui-même ne peut pas vous dire son nom, et il ne s'en soucie pas vraiment - donc la seule façon de savoir comment il s'appelle est de demandez à tous vos voisins (espaces de noms) si c'est leur chat (objet) ...

.... et ne soyez pas surpris si vous constatez qu'il est connu sous de nombreux noms, ou pas de nom du tout!

Fredrik Lundh, 3 novembre 2000, en réponse à la question "Comment puis-je obtenir le nom d'une variable de C++ quand j'ai le PyObject *?"

26
Johan Buret

Pour ajouter à @ la réponse de Jay , quelques concepts ...

Les "variables" Python sont simplement des références à des valeurs. Chaque valeur occupe un emplacement mémoire donné (voir id())

>>> id(1)
10052552

>>> sys.getrefcount(1)
569

De ce qui précède, vous pouvez remarquer que la valeur "1" est présente à l'emplacement de mémoire 10052552. Il est fait référence à 569 fois dans cette instance de l'interpréteur.

>>> MYVAR = 1
>>> sys.getrefcount(1)
570

Maintenant, voyez que parce qu'un autre nom est lié à cette valeur, le nombre de références a augmenté de un.

Sur la base de ces faits, il n'est pas réaliste/possible de dire quel nom de variable unique pointe vers une valeur.

Je pense que la meilleure façon de résoudre votre problème est d'ajouter un mappage et une fonction à votre référence enum à un nom de chaîne.

myEnum.get_name(myEnum.SomeNameA) 

Veuillez commenter si vous souhaitez un exemple de code.

23
gahooa

Utilisez simplement le texte que vous souhaitez imprimer comme valeur de l'énumération, comme dans

class MyEnum (object):
    valueA = "valueA"
    valueB = "valueB"

la comparaison des chaînes pour l'identité est presque aussi efficace dans Python que la comparaison des valeurs entières (cela est dû au fait que les chaînes sont immuables comme ont une valeur de hachage)

Bien sûr, il existe des moyens plus simples de créer l'énumération en premier lieu:

class Enum (object):
    def __init__(self, *values):
        for v in values:
            self.__dict__[v] = v

Ensuite, créez votre énumération comme ceci:

MyEnum = Enum("valueA", "valueB")

et accédez de la même manière que ci-dessus:

MyEnum.valueA
6
Ber

Il y a deux réponses à cette question: Oui et Non .

Pouvez-vous le faire? - Oui (dans la plupart des cas) 
 Cela en vaut-il la peine? - Non (dans la plupart des cas) 

Comment faire:

Cela dépend d'une implémentation d'énumérations. Par exemple:

class Enum:
    A = 1
    B = 2

Peu importe qu'il y ait 100 noms se réfère à un entier dont vous souhaitez trouver le nom si vous connaissez l'objet de classe enums et tous leurs noms (ou au moins un préfixe unique ) dans cette classe .

Pour l'exemple ci-dessus comme l'a suggéré @batbrat vous pouvez inspecter 'Enum.__dict__' En utilisant namestr() :

 >>> getEnum = lambda: Enum.A
 >>> namestr(getEnum(), Enum.__dict__)
 >>> ['A']

Dans ce cas, vous n'avez même pas besoin de connaître tous les noms d'énumération. Si les énumérations sont implémentées différemment, vous devrez peut-être utiliser un hack différent. Il y aura une solution dans la plupart des cas, par exemple, voir @ réponse de Jay . Mais ça n'a pas d'importance parce que ..

Est-ce que ça vaut le coup?

  • Certaines implémentations enum peuvent nécessiter des hacks laids, complexes et finalement peu fiables pour implémenter namestr().

  • La classe Enum peut renvoyer la réponse par elle-même (voir @ David's , @ Ber's , @ gahooa's réponses).

  • Comme @ Ber l'a souligné il n'y a pas d'instruction Enum et switch intégrée dans Python (voir PEP-0275 , PEP- 31 ). Il vaut la peine d'étudier des solutions sans énumération.

3
jfs

Oui.

Dans votre cas avec des énumérations, c'est simple:

>>> from enum import Enum
>>> class Type(Enum):
...   AB, SBS, INTERLACED, SPLIT = range(4)
...
>>> Type.AB
<Type.AB: 0>
>>> print(Type.AB)
Type.AB
>>> Type.AB.name
'AB'
>>> Type.AB.value
0
>>> Type(0)
<Type.AB: 0>

En général, cela pourrait être ambigu:

>>> def varname(v): d = globals(); return [k for k in d if d[k] == v]
...
>>> def varnameis(v): d = globals(); return [k for k in d if d[k] is v]
...
>>> foo = {'a':'aap'}
>>> bar = {'a':'aap'}
>>> varname(foo)
['foo', 'bar']
>>> varnameis(foo)
['foo']

Ou même trompeur:

>>> foo = "A"; bar = "A"
>>> varnameis("A")
['foo', 'bar']
>>> foo = "An immutable value."; bar = "An immutable value."
>>> varnameis("An immutable value.")
[]
>>> foo = "An immutable value."; bar = "An immutable value."; varnameis("An immutable value.")
['foo', 'bar']
>>> import sys; sys.version
'3.6.6 (v3.6.6:4cf1f54eb7, Jun 27 2018, 02:47:15) [MSC v.1900 32 bit (Intel)]'

Pour rechercher dans n'importe quelle portée, utilisez ceci:

>>> def varname(v, scope=None): d = globals() if not scope else vars(scope); return [k for k in d if d[k] == v]
...
>>> import time
>>> time.timezone
-3600
>>> varname(-3600)
[]
>>> varname(-3600, time)
['timezone']
3
Cees Timmerman

Vous pouvez stocker le nom canonique en tant qu'attribut de l'instance, puis l'affecter à une variable du même nom. Cela pourrait fonctionner:

class MyEnum(object):
    def __new__(cls, name):
        try:
            return getattr(MyEnum, name)
        except AttributeError:
            e = super(MyEnum, cls).__new__(cls)
            e.name = name
            setattr(MyEnum, name, e)
            return e

Quoi qu'il en soit, ce n'est pas une chose particulièrement "Pythonique" à faire.

2
David Z

À la réflexion:

Étant donné que Python ne fournit pas de types Enum natifs, vous ne devriez pas en demander un, mais utiliser à la place d'autres constructions plus puissantes pour construire votre programme. Sinon, la prochaine étape sera invariablement "Why does = Python n'a pas d'instruction switch ...:, Et comment puis-je l'émuler au mieux? "

Comme les énumérations sont souvent utilisées pour définir une sorte d'état, une bien meilleure approche est la suivante: créer une classe de base qui définit toutes les propriétés abstraites, les attributs et les méthodes appartenant à un état. Ensuite, pour chaque état, dérivez une sous-classe qui implémente le comportement spécifique de cet état. Vous pouvez ensuite passer ces classes (ou peut-être des instances de celles-ci) pour gérer l'état et son comportement.

Si vous utilisez des classes au lieu d'instances (la manière Python d'un "singleton"), vous pouvez simplement vérifier n'importe quel état donné (pas que cela devrait être nécessaire) par if current_state is StateA: (notez le is au lieu de ==) sans pénalité de performance par rapport à la comparaison de valeurs entières.

Et bien sûr, vous pouvez définir un attribut name et une méthode __str__() pour accéder et imprimer le nom de l'état.

2
Ber

Pour autant que je sache, cela nécessitera une certaine introspection. Vous pouvez essayer d'utiliser le module d'inspection.

Il y a quelques choses simples que vous voudrez peut-être essayer avant cela:

  1. Ce n'est pas le code exact qui devrait être utilisé. Vous devrez récupérer le nom de la variable dans __dict__ avant d'imprimer.
     print myEnum .__ dict __ 
     
  2. Si vous souhaitez imprimer le nom de la variable depuis l'intérieur de la fonction, vous pouvez essayer les fonctions - vars (), locals () et globals ().
    Modifier :
    Je viens de remarquer que ce n'est pas ce que vous voulez. Dans ce cas, l'ajout d'un dict et d'une fonction peut fonctionner
  3. Vous pouvez imprimer le __dict__ de la classe de vos énumérations.

Cela dit, il n'y a pas d'énumérations standard en Python. Il serait utile de savoir comment vous les créez.

Après réflexion, vous pouvez conserver vos variables sous forme de dictionnaire dans l'énumération, saisies par nom de variable et fournir une méthode d'énumération pour trouver la bonne variable et imprimer son nom. Cette solution (garder un dict) est mauvaise car les valeurs des variables ne sont pas nécessairement uniques.

Modifier :
Le problème n'est pas anodin, vous pouvez donc utiliser une solution éprouvée. À mon humble avis, vous feriez mieux d'éviter la situation si vous le pouvez.

0
batbrat

Erlang a un concept appelé "atomes" - ils sont similaires aux constantes de chaîne ou aux énumérations. Envisagez d'utiliser une constante de chaîne comme valeur de votre énumération - la même chose que le nom de l'énumération.

0
gahooa

OUI

import inspect

def printVariableName(abc):
    newvar=inspect.stack()[1][4][0]
    print(newvar.split("(")[1].split(")")[0])

noob=10
printVariableName(noob)

Production:

noob
0
ProNoob