Получение имени переменной в виде строки

Этот поток обсуждает, как получить имя функции в виде строки в Python: Как получить имя функции в виде строки в Python?

Как я могу сделать то же самое для переменной? В отличие от функций, переменные Python не имеют атрибута __name__.

Другими словами, если у меня есть переменная, такая как:

foo = dict()
foo['bar'] = 2

Я ищу функцию/атрибут, например, retrieve_name:

retrieve_name(foo) 

которая возвращает строку 'foo'

Обновление:

Так как люди спрашивают, почему я хочу это сделать, вот пример. Я хотел бы создать DataFrame в Pandas из этого списка, где имена столбцов задаются именами реальных словарей:

# List of dictionaries for my DataFrame
list_of_dicts = [n_jobs, users, queues, priorities]

Ответ 1

Единственными объектами в Python, которые имеют канонические имена, являются модули, функции и классы, и, конечно же, нет гарантии, что это каноническое имя имеет какое-либо значение в любом пространстве имен после того, как функция или класс были определены или импортирован модуль. Эти имена также могут быть изменены после создания объектов, поэтому они не всегда могут быть особенно надежными.

То, что вы хотите сделать, невозможно без рекурсивной ходьбы по дереву именованных объектов; имя является односторонней ссылкой на объект. Обычный или садовый объект Python не содержит ссылок на его имена. Представьте себе, что если каждое целое число, каждый dict, каждый список, каждый Boolean должен содержать список строк, которые представляли имена, которые ссылались на него! Это будет кошмар внедрения, с небольшим преимуществом для программиста.

Ответ 2

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

Кто-то упомянул в этом ответе, что вам, возможно, придется пройтись по стеку и проверить все локальные и глобальные переменные, чтобы найти foo, но если foo назначен в области, в которой вы вызываете эту функцию retrieve_name, вы можете использовать inspect current frame чтобы получить все из этих локальных переменных.

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

import inspect

x,y,z = 1,2,3

def retrieve_name(var):
    callers_local_vars = inspect.currentframe().f_back.f_locals.items()
    return [var_name for var_name, var_val in callers_local_vars if var_val is var]

print retrieve_name(y)

Если вы вызываете эту функцию из другой функции, что-то вроде:

def foo(bar):
    return retrieve_name(bar)

foo(baz)

И вам нужна baz вместо bar, вам просто нужно вернуться назад. Это можно сделать, добавив дополнительный .f_back в инициализации caller_local_vars.

Смотрите пример здесь: ideone

Ответ 3

Я не верю, что это возможно. Рассмотрим следующий пример:

>>> a = []
>>> b = a
>>> id(a)
140031712435664
>>> id(b)
140031712435664

И a b указывают на тот же объект, но объект не может знать, какие переменные указывают на него.

Ответ 4

В python3 эта функция получит внешнее большинство имен в стеке:

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]

Это полезно в любом месте кода. Перемещает обратную стек, ищущую первое совпадение.

Ответ 5

def name(**variables):
    return [x for x in variables]

Он используется следующим образом:

name(variable=variable)

Ответ 6

Здесь один подход. Я бы не рекомендовал это ни для чего важного, потому что он будет довольно хрупким. Но это можно сделать.

Создайте функцию, которая использует модуль inspect, чтобы найти исходный код, который его вызвал. Затем вы можете проанализировать исходный код, чтобы идентифицировать имена переменных, которые вы хотите получить. Например, здесь функция под названием autodict, которая принимает список переменных и возвращает имена переменных сопоставления словаря своим значениям. Например:.

x = 'foo'
y = 'bar'
d = autodict(x, y)
print d

Давал бы:

{'x': 'foo', 'y': 'bar'}

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

Во всяком случае здесь код:

def autodict(*args):
    get_rid_of = ['autodict(', ',', ')', '\n']
    calling_code = inspect.getouterframes(inspect.currentframe())[1][4][0]
    calling_code = calling_code[calling_code.index('autodict'):]
    for garbage in get_rid_of:
        calling_code = calling_code.replace(garbage, '')
    var_names, var_values = calling_code.split(), args
    dyn_dict = {var_name: var_value for var_name, var_value in
                zip(var_names, var_values)}
    return dyn_dict

Действие происходит в строке с inspect.getouterframes, которая возвращает строку в коде, который называется autodict.

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

Ответ 7

>>> locals()['foo']
{}
>>> globals()['foo']
{}

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

Если ваша переменная находится в классе, вы можете использовать className. dict.keys() или vars (self), чтобы увидеть, была ли определена ваша переменная.

Ответ 8

Я написал пакет волшебства, чтобы творить магию такого рода. Ты можешь написать:

from sorcery import dict_of

columns = dict_of(n_jobs, users, queues, priorities)

и передать это конструктору dataframe. Это эквивалентно:

columns = dict(n_jobs=n_jobs, users=users, queues=queues, priorities=priorities)

Ответ 9

В Python ключевые слова def и class свяжут определенное имя с объектом, который они определяют (функция или класс). Аналогичным образом, модули получают имя из-за того, что в файловой системе называют что-то конкретное. Во всех трех случаях существует очевидный способ назначить "каноническое" имя рассматриваемому объекту.

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

Python не сохраняет имя в левой части назначения в назначенный объект, потому что:

  • Это потребует выяснения, какое имя было "каноническим" среди нескольких конфликтующих объектов,
  • Это не имеет смысла для объектов, которые никогда не назначаются явному имени переменной,
  • Это было бы крайне неэффективно,
  • Буквально ни один другой язык не существует.

Так, например, функции, определенные с помощью lambda, всегда будут иметь "имя" <lambda>, а не определенное имя функции.

Лучшим подходом было бы просто попросить вызывающего абонента передать (необязательный) список имен. Если ввести '...','...' слишком громоздко, вы можете принять, например. одна строка, содержащая список имен, разделенных запятыми (например, namedtuple).

Ответ 10

Мне кажется, что это сложно сделать в Python из-за простого факта, что вы никогда не узнаете имя переменной, которую используете. Итак, в его примере вы можете сделать:

Вместо:

list_of_dicts = [n_jobs, users, queues, priorities]

dict_of_dicts = {"n_jobs" : n_jobs, "users" : users, "queues" : queues, "priorities" : priorities}

Ответ 11

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

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

def retrieve_name(x, Vars=vars()):
    for k in Vars:
        if type(x) == type(Vars[k]):
            if x is Vars[k]:
                return k
    return None

Ответ 12

Эта функция напечатает имя переменной со значением:

import inspect

def print_this(var):
    callers_local_vars = inspect.currentframe().f_back.f_locals.items()
    print(str([k for k, v in callers_local_vars if v is var][0])+': '+str(var))
***Input & Function call:***
my_var = 10

print_this(my_var)

***Output**:*
my_var: 10

Ответ 13

Если цель состоит в том, чтобы помочь вам отслеживать ваши переменные, вы можете написать простую функцию, которая помечает переменную и возвращает ее значение и тип. Например, предположим, что i_f = 3.01, и вы округлите его до целого числа i_n для использования в коде, а затем вам понадобится строка i_s, которая войдет в отчет.

def whatis(string, x):
    print(string+' value=',repr(x),type(x))
    return string+' value='+repr(x)+repr(type(x))
i_f=3.01
i_n=int(i_f)
i_s=str(i_n)
i_l=[i_f, i_n, i_s]
i_u=(i_f, i_n, i_s)

## make report that identifies all types
report='\n'+20*'#'+'\nThis is the report:\n'
report+= whatis('i_f ',i_f)+'\n'
report+=whatis('i_n ',i_n)+'\n'
report+=whatis('i_s ',i_s)+'\n'
report+=whatis('i_l ',i_l)+'\n'
report+=whatis('i_u ',i_u)+'\n'
print(report)

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

Я новичок в Python и нашел этот очень полезный способ записывать свои усилия во время программирования и пытаться справиться со всеми объектами в Python. Один недостаток заключается в том, что whatis() завершается ошибкой, если вызывает функцию, описанную вне процедуры, в которой он используется. Например, int (i_f) был допустимым вызовом функции только потому, что функция int известна Python. Вы можете вызвать whatis() с помощью int (i_f ** 2), но если по какой-то странной причине вы решите определить функцию с именем int_squared, она должна быть объявлена внутри процедуры, где используется whatis().

Ответ 14

Для констант вы можете использовать перечисление, которое поддерживает получение его имени.

Ответ 15

Я пытаюсь получить имя от осмотра локальных жителей, но он не может обработать var like a [1], b.val. После этого у меня появилась новая идея - получить имя var из кода, и я попробую его succ! код как ниже:

#direct get from called function code
def retrieve_name_ex(var):
    stacks = inspect.stack()
    try:
        func = stacks[0].function
        code = stacks[1].code_context[0]
        s = code.index(func)
        s = code.index("(", s + len(func)) + 1
        e = code.index(")", s)
        return code[s:e].strip()
    except:
        return ""

Ответ 16

Вы можете попробовать следующее, чтобы получить имя функции, которую вы определили (хотя она не работает для встроенных функций):

import re
def retrieve_name(func):
    return re.match("<function\s+(\w+)\s+at.*", str(func)).group(1)

def foo(x):
    return x**2

print(retrieve_name(foo))
# foo