Более простой способ создать словарь отдельных переменных? - PullRequest
217 голосов
/ 31 марта 2010

Я хотел бы иметь возможность получить имя переменной в виде строки, но я не знаю, обладает ли Python такими возможностями самоанализа. Что-то вроде:

>>> print(my_var.__name__)
'my_var'

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

bar = True
foo = False
>>> my_dict = dict(bar=bar, foo=foo)
>>> print my_dict 
{'foo': False, 'bar': True}

Но я бы хотел что-то более автоматическое, чем это.

Python имеет locals() и vars(), так что я думаю, что есть способ.

Ответы [ 25 ]

5 голосов
/ 31 августа 2015

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

def varname(var):
  import inspect
  frame = inspect.currentframe()
  var_id = id(var)
  for name in frame.f_back.f_locals.keys():
    try:
      if id(eval(name)) == var_id:
        return(name)
    except:
      pass
4 голосов
/ 12 апреля 2012

Читая ветку, я увидел очень много трений. Это достаточно легко дать плохой ответ, тогда пусть кто-нибудь даст правильный ответ. Во всяком случае, вот что я нашел.

От: [effbot.org] (http://effbot.org/zone/python-objects.htm#names)

Имена немного отличаются - они на самом деле не являются свойствами объекта, а сам объект не знает, как он называется.

Объект может иметь любое количество имен или вообще не иметь имени.

Имена располагаются в пространствах имен (таких как пространство имен модуля, пространство имен экземпляра, локальное пространство имен функции).

Примечание: он говорит, что сам объект не знает, как он называется , так что это был ключ. Объекты Python не являются самоссылочными. Затем он говорит: Имена живут в пространствах имен . У нас это есть в TCL / TK. Поэтому, возможно, мой ответ поможет (но он мне помог)


    jj = 123
    print eval("'" + str(id(jj)) + "'")
    print dir()

166707048
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'jj']

Итак, в конце списка стоит «jj».

Перепишите код как:


    jj = 123
    print eval("'" + str(id(jj)) + "'")
    for x in dir():
        print id(eval(x))

161922920
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'jj']
3077447796
136515736
3077408320
3077656800
136515736
161922920

Этот неприятный бит кода идентифицирует имя переменной / объекта / что угодно, что вы называете педантикой.

Итак, вот оно. Адрес памяти 'jj' тот же, когда мы ищем его напрямую, как и когда мы ищем словарь в глобальном пространстве имен. Я уверен, что вы можете сделать функцию для этого. Просто запомните, в каком пространстве имен находится ваша переменная / объект / wypci.

QED.

2 голосов
/ 22 января 2018

Я загрузил решение в pypi . Это модуль, определяющий эквивалент функции C # nameof.

Он перебирает инструкции байт-кода для вызываемого фрейма, получая имена переменных / атрибутов, переданных ему. Имена находятся в инструкциях .argrepr из LOAD после имени функции.

2 голосов
/ 18 сентября 2016
import re
import traceback

pattren = re.compile(r'[\W+\w+]*get_variable_name\((\w+)\)')
def get_variable_name(x):
    return pattren.match( traceback.extract_stack(limit=2)[0][3]) .group(1)

a = 1
b = a
c = b
print get_variable_name(a)
print get_variable_name(b)
print get_variable_name(c)
2 голосов
/ 15 июля 2018

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

from sorcery import dict_of

my_dict = dict_of(foo, bar)
1 голос
/ 05 сентября 2012

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

И почему это было так необходимо?

Короче говоря, я строил плагин для Maya . Основной плагин был построен с использованием C ++, но графический интерфейс рисуется через Python (так как он не интенсивно использует процессор). Так как я пока не знаю, как return несколько значений из плагина, кроме значения по умолчанию MStatus, поэтому для обновления словаря в Python мне пришлось передать имя переменной, указывая на объект, реализующий графический интерфейс и содержащий сам словарь, подключаемому модулю, а затем используйте MGlobal::executePythonCommand() для обновления словаря из глобальной области видимости Maya .

Чтобы сделать то, что я сделал, было что-то вроде:

import time

class foo(bar):

    def __init__(self):
        super(foo, self).__init__()
        self.time = time.time() #almost guaranteed to be unique on a single computer

    def name(self):
        g = globals()
        for x in g:
            if isinstance(g[x], type(self)):
                if g[x].time == self.time:
                    return x
                    #or you could:
                    #return filter(None,[x if g[x].time == self.time else None for x in g if isinstance(g[x], type(self))])
                    #and return all keys pointing to object itself

Я знаю, что это не идеальное решение в globals, когда многие клавиши могут указывать на один и тот же объект , например ::

a = foo()
b = a
b.name()
>>>b
or
>>>a

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

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

Я пробовал это на int (примитивный целочисленный класс), но проблема в том, что эти примитивные классы не обойдутся (пожалуйста, исправьте используемую техническую терминологию, если она неверна). Вы можете повторно реализовать int, а затем выполнить int = foo, но a = 3 никогда не будет объектом foo, но примитивом. Чтобы преодолеть это, вы должны a = foo(3) заставить a.name() работать.

1 голос
/ 30 июня 2016

Вы можете использовать easydict

>>> from easydict import EasyDict as edict
>>> d = edict({'foo':3, 'bar':{'x':1, 'y':2}})
>>> d.foo
3
>>> d.bar.x
1
>>> d = edict(foo=3)
>>> d.foo
3

другой пример:

>>> d = EasyDict(log=False)
>>> d.debug = True
>>> d.items()
[('debug', True), ('log', False)]
1 голос
/ 31 марта 2010

Большинство объектов не имеют атрибута __name __ . (Классы, функции и модули делают; есть еще встроенные типы, которые есть?)

Что еще вы ожидаете от print(my_var.__name__), кроме print("my_var")? Вы можете просто использовать строку напрямую?

Вы можете «нарезать» диктовку:

def dict_slice(D, keys, default=None):
  return dict((k, D.get(k, default)) for k in keys)

print dict_slice(locals(), ["foo", "bar"])
# or use set literal syntax if you have a recent enough version:
print dict_slice(locals(), {"foo", "bar"})

В качестве альтернативы:

throw = object()  # sentinel
def dict_slice(D, keys, default=throw):
  def get(k):
    v = D.get(k, throw)
    if v is not throw:
      return v
    if default is throw:
      raise KeyError(k)
    return default
  return dict((k, get(k)) for k in keys)
1 голос
/ 01 сентября 2013

Я работал над похожей проблемой. @ S.Lott сказал: «Если у вас есть список переменных, какой смысл« открывать »их имена?» И мой ответ - просто посмотреть, можно ли это сделать, и если по какой-то причине вы хотите отсортировать переменные по типу в списках. Так или иначе, в моем исследовании я натолкнулся на эту тему, и мое решение немного расширено и основано на решении @rlotun. @Unutbu сказал: «Эта идея имеет свои достоинства, но учтите, что если два имени переменных ссылаются на одно и то же значение (например, True), то может быть возвращено непреднамеренное имя переменной». В этом упражнении это было правдой, поэтому я рассмотрел его, используя понимание списка, подобное этому для каждой возможности: isClass = [i for i in isClass if i != 'item']. Без этого «элемент» будет отображаться в каждом списке.

__metaclass__ = type

from types import *

class Class_1: pass
class Class_2: pass
list_1 = [1, 2, 3]
list_2 = ['dog', 'cat', 'bird']
tuple_1 = ('one', 'two', 'three')
tuple_2 = (1000, 2000, 3000)
dict_1 = {'one': 1, 'two': 2, 'three': 3}
dict_2 = {'dog': 'collie', 'cat': 'calico', 'bird': 'robin'}
x = 23
y = 29
pie = 3.14159
eee = 2.71828
house = 'single story'
cabin = 'cozy'

isClass = []; isList = []; isTuple = []; isDict = []; isInt = []; isFloat = []; isString = []; other = []

mixedDataTypes = [Class_1, list_1, tuple_1, dict_1, x, pie, house, Class_2, list_2, tuple_2, dict_2, y, eee, cabin]

print '\nMIXED_DATA_TYPES total count:', len(mixedDataTypes)

for item in mixedDataTypes:
    try:
        # if isinstance(item, ClassType): # use this for old class types (before 3.0)
        if isinstance(item, type):
            for k, v in list(locals().iteritems()):
                if v is item:
                    mapping_as_str = k
                    isClass.append(mapping_as_str)
            isClass = [i for i in isClass if i != 'item']

        elif isinstance(item, ListType):
            for k, v in list(locals().iteritems()):
                if v is item:
                    mapping_as_str = k
                    isList.append(mapping_as_str)
            isList = [i for i in isList if i != 'item']

        elif isinstance(item, TupleType):
            for k, v in list(locals().iteritems()):
                if v is item:
                    mapping_as_str = k
                    isTuple.append(mapping_as_str)
            isTuple = [i for i in isTuple if i != 'item']

        elif isinstance(item, DictType):
            for k, v in list(locals().iteritems()):
                if v is item:
                    mapping_as_str = k
                    isDict.append(mapping_as_str)
            isDict = [i for i in isDict if i != 'item']

        elif isinstance(item, IntType):
            for k, v in list(locals().iteritems()):
                if v is item:
                    mapping_as_str = k
                    isInt.append(mapping_as_str)
            isInt = [i for i in isInt if i != 'item']

        elif isinstance(item, FloatType):
            for k, v in list(locals().iteritems()):
                if v is item:
                    mapping_as_str = k
                    isFloat.append(mapping_as_str)
            isFloat = [i for i in isFloat if i != 'item']

        elif isinstance(item, StringType):
            for k, v in list(locals().iteritems()):
                if v is item:
                    mapping_as_str = k
                    isString.append(mapping_as_str)
            isString = [i for i in isString if i != 'item']

        else:
            for k, v in list(locals().iteritems()):
                if v is item:
                    mapping_as_str = k
                    other.append(mapping_as_str)
            other = [i for i in other if i != 'item']

    except (TypeError, AttributeError), e:
        print e

print '\n isClass:', len(isClass), isClass
print '  isList:', len(isList), isList
print ' isTuple:', len(isTuple), isTuple
print '  isDict:', len(isDict), isDict
print '   isInt:', len(isInt), isInt
print ' isFloat:', len(isFloat), isFloat
print 'isString:', len(isString), isString
print '   other:', len(other), other

# my output and the output I wanted
'''
MIXED_DATA_TYPES total count: 14

 isClass: 2 ['Class_1', 'Class_2']
  isList: 2 ['list_1', 'list_2']
 isTuple: 2 ['tuple_1', 'tuple_2']
  isDict: 2 ['dict_1', 'dict_2']
   isInt: 2 ['x', 'y']
 isFloat: 2 ['pie', 'eee']
isString: 2 ['house', 'cabin']
   other: 0 []
'''
1 голос
/ 16 августа 2013

Может быть, я обдумываю это, но ..

str_l = next((k for k,v in locals().items() if id(l) == id(v)))


>>> bar = True
>>> foo = False
>>> my_dict=dict(bar=bar, foo=foo)
>>> next((k for k,v in locals().items() if id(bar) == id(v)))
'bar'
>>> next((k for k,v in locals().items() if id(foo) == id(v)))
'foo'
>>> next((k for k,v in locals().items() if id(my_dict) == id(v)))
'my_dict'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...