Могу ли я напечатать оригинальное имя переменной в Python? - PullRequest
50 голосов
/ 13 февраля 2009

У меня есть enum и я использую переменные, такие как myEnum.SomeNameA, myEnum.SomeNameB и т. Д. Когда я возвращаю одну из этих переменных из функции, могу ли я напечатать их имена (например, myEnum.SomeNameA) вместо значения, которое они вернули

Ответы [ 10 ]

32 голосов
/ 13 февраля 2009

Краткий ответ: нет.

Длинный ответ: это возможно при некоторых уродливых хакерских атаках, использующих traceback, inspect и т. П., Но обычно это не рекомендуется для производственного кода Например, см .:

Возможно, вы можете использовать обходной путь для перевода значения обратно в строку имени / представления. Если вы опубликуете еще несколько примеров кода и информацию о том, для чего вы этого хотите, возможно, мы можем предоставить более подробную помощь.

26 голосов
/ 13 февраля 2009

Нет такого понятия, как уникальное или оригинальное имя переменной http://www.amk.ca/quotations/python-quotes/page-8

Точно так же, как вы получаете имя того кота, которого вы нашли на крыльце: сам кот (объект) не может сказать вам свое имя, и ему все равно - так что единственный способ узнать, что это Вызванный - спросить всех ваших соседей (пространства имен), является ли это их кошкой (объектом) ...

.... и не удивляйтесь, если вы обнаружите, что он известен под многими именами или вообще без имени!

Фредрик Лунд, 3 ноября 2000 года, в ответ на вопрос «Как получить имя переменной из C ++, когда у меня есть PyObject *?»

22 голосов
/ 13 февраля 2009

Чтобы добавить к @ ответ Джея , некоторые понятия ...

«Переменные» Python - это просто ссылки на значения. Каждое значение занимает определенную область памяти (см. id())

>>> id(1)
10052552

>>> sys.getrefcount(1)
569

Исходя из вышеизложенного, вы можете заметить, что значение «1» присутствует в ячейке памяти 10052552. В этом случае интерпретатора упоминается 569 раз.

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

Теперь обратите внимание, что поскольку к этому значению привязано еще одно имя, счетчик ссылок увеличился на единицу.

Исходя из этих фактов, нереально / невозможно сказать, какое имя отдельной переменной указывает на значение.

Я думаю, что лучший способ решить вашу проблему - добавить отображение и функцию к вашей ссылке на перечисление обратно к строковому имени.

myEnum.get_name(myEnum.SomeNameA) 

Пожалуйста, прокомментируйте, если вам нужен пример кода.

6 голосов
/ 13 февраля 2009

Просто используйте текст, который вы хотите напечатать, в качестве значения перечисления, как в

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

сравнение строк на идентичность почти так же эффективно в Python, как и сравнение целочисленных значений (это связано с тем, что строки являются неизменяемыми и имеют хеш-значения)

Конечно, существуют простые способы создания перечисления:

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

Затем создайте перечисление следующим образом:

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

и доступ аналогичны указанным выше:

MyEnum.valueA
3 голосов
/ 14 сентября 2018

Да.

В вашем случае с перечислениями это просто:

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

В общем, это может быть неоднозначно:

>>> 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']

Или даже вводит в заблуждение:

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

Для поиска в любой области используйте это:

>>> 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 голосов
/ 27 февраля 2009

На этот вопрос есть два ответа: Да и Нет .

Can you do it?  -- Yes (in most cases)
Is it worth it? -- No  (in most cases)

Как это сделать:

Это зависит от реализации перечислений. Например:

class Enum:
    A = 1
    B = 2

Не имеет значения, относится ли 100 имен к целому числу, имя которого вы хотите найти , если вы знаете перечисление объекта класса и все они являются именами (или, по крайней мере, уникальным префиксом) в этом класс .

Для приведенного выше примера , как предложил @batbrat , вы можете проверить 'Enum.__dict__', используя namestr():

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

В этом случае вам даже не нужно знать все имена перечислений. Если перечисления реализованы по-другому, то вам может понадобиться другой хак. В большинстве случаев будет какое-то решение, например, см. @ Jay's answer . Но это не имеет значения, потому что ..

Оно того стоит?

  • Некоторые реализации enum могут потребовать уродливых, сложных и ненадежных хаков для реализации namestr().

  • Класс enum может возвращать ответ сам (см. @ David's , @ Ber's , @ gahooa's ответы).

  • Как и @ Ber указал , в Python нет встроенного оператора Enum и оператора switch (см. PEP-0275 , PEP-3103 ). Стоит исследовать решения без перечислений.

2 голосов
/ 13 февраля 2009

По второй мысли:

Поскольку Python не предоставляет нативные типы Enum, вам не следует запрашивать один, а вместо этого использовать другую, более мощную конструкцию для сборки вашей программы. В противном случае следующим шагом всегда будет «Почему у Python нет оператора switch ...:, и как мне лучше всего его эмулировать?»

Поскольку перечисления часто используются для определения какого-либо состояния, гораздо лучший подход заключается в следующем: Создайте базовый класс, который определяет все абстрактные свойства, атрибуты и методы, принадлежащие состоянию. Затем для каждого состояния выведите подкласс, который реализует конкретное поведение этого состояния. Затем вы можете передать эти классы (или, возможно, их экземпляры) для обработки состояния и его поведения.

Если вы используете классы вместо экземпляров (способ «синглтон» в Python), вы можете просто проверить любое состояние (не то, что это должно быть необходимо) с помощью if current_state is StateA: (обратите внимание на is вместо ==) без потери производительности при сравнении целочисленных значений.

И, конечно, вы можете определить атрибут name и метод __str__() для доступа и печати названия состояния.

2 голосов
/ 13 февраля 2009

Вы можете сохранить каноническое имя в качестве атрибута экземпляра, а затем назначить его переменной с тем же именем. Это может сработать:

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

Несмотря на это, это не особо "Pythonic" вещь, чтобы сделать.

0 голосов
/ 13 февраля 2009

У Эрланга есть понятие, называемое «атомы» - они похожи на строковые константы или перечисления. Попробуйте использовать строковую константу в качестве значения вашего перечисления - то же самое, что и имя перечисления.

0 голосов
/ 13 февраля 2009

Насколько я знаю, это потребует некоторого самоанализа. Вы можете попробовать использовать модуль проверки.

Есть несколько простых вещей, которые вы можете попробовать до этого:

  1. Это не точный код, который следует использовать. Вам придется извлечь имя переменной из __dict__ перед печатью.
    print myEnum.__dict__
            
  2. Если вы хотите напечатать имя переменной внутри функции, вы можете попробовать функции - vars (), locals () и globals ().
    Изменить :
    Я просто заметил, что это не то, что вы хотите. В этом случае добавление дикт и функция может работать
  3. Вы можете распечатать __dict__ класса ваших перечислений.

Все это говорит о том, что в Python нет стандартных перечислений. Это помогло бы узнать, как вы их создаете.

Подумав, вы можете сохранить свои переменные в качестве словаря в перечислении с указанием имени переменной и предоставить метод перечисления, чтобы найти нужную переменную и вывести ее имя. Это решение (сохранение разборчивости) плохое, поскольку значения переменных не обязательно уникальны.

Редактировать :
Проблема не является тривиальной, поэтому вы можете использовать проверенное решение. ИМХО, тебе лучше избежать ситуации, если сможешь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...