У меня есть список имен переменных, например:
['foo', 'bar', 'baz']
(Изначально я спросил, как преобразовать список переменных. См. Ответ Грега Хьюджилла ниже.)
Как преобразовать это в словарь, где ключи - это имена переменных (в виде строк), а значения - это значения переменных?
{'foo': foo, 'bar': bar, 'baz': baz}
Теперь, когда я снова задаю вопрос, я придумал:
d = {}
for name in list_of_variable_names:
d[name] = eval(name)
Можно ли это улучшить?
Обновлять, отвечая на вопрос (в комментарии) о том, почему я хочу это сделать:
Я часто использую оператор% для строк со словарем имен и значений для интерполяции. Часто имена в строке - это просто имена локальных переменных. Итак (с ответом ниже) я могу сделать что-то вроде этого:
message = '''Name: %(name)s
ZIP: %(zip)s
Dear %(name)s,
...''' % dict((x, locals()[x]) for x in ['name', 'zip'])
Ответ на обновление в ответ на мой комментарий: Идиома Python для этого (которую я не поддерживаю, но я видел, как использовалась) - это напрямую "string% locals ()".
Вы можете заменить dict((x, locals()[...)
просто vars()
.
Пример: a, b = 1, 'astring'; print("%(a)s %(b)s %(b)r" % vars())
Ваш исходный список [foo, bar, baz]
не содержит переменной имена, он просто содержит элементы, которые ссылаются на тот же значения, что и перечисленные вами переменные. Это потому, что у вас может быть два разных имени переменных, которые ссылаются на одно и то же значение.
Таким образом, список сам по себе не содержит информации о том, какие другие имена относятся к объектам. Первый элемент в вашем массиве имеет имя foo
, но он также имеет имя a[0]
(при условии, что ваш массив называется a
). После выполнения следующего кода quux
также обращается к тому же объекту:
quux = a[0]
Обновление: вы правы, что для этого можно использовать eval()
, но его использование обычно не рекомендуется. Python предоставляет специальный член с именем __dict__
, который содержит таблицу символов для текущего модуля. Так что вы можете:
import __main__
d = dict((x, __main__.__dict__[x]) for x in list_of_variable_names)
Наличие import __main__
, когда ваш код находится в безымянном основном модуле, - это причуда Python.
Вы можете использовать составные части списка или генератора для создания списка кортежей ключей и значений, используемых для непосредственного создания экземпляра dict. Лучший способ - ниже:
dict((name, eval(name)) for name in list_of_variable_names)
Кроме того, если вы знаете, например, что переменные существуют в локальной таблице символов, вы можете спастись от опасного eval, просмотрев переменную непосредственно у локальных переменных:
dict((name, locals()[name]) for name in list_of_variable_names)
После вашего последнего обновления, я думаю, что приведенный ниже ответ действительно то, что вам нужно. Если вы просто используете это для расширения строки с помощью строк, которые вы контролируете, просто передайте locals () непосредственно в расширение строки, и она выберет желаемые значения.
Если, однако, эти строки могут когда-либо поступать из внешнего источника (например, файлов перевода), тогда рекомендуется фильтровать locals ()
поскольку в dict могут быть неиспользуемые ключи, вы можете просто использовать "..."% locals () без фильтрации
Согласны, "..."% locals () тоже гораздо более питонический, в том смысле, что это обычная идиома.
Я заставил это работать, используя dict ([(name, locals () [name]) для имени в list_of_variable_names])
Неэффективно, но без вызова eval
:
dict((k,v) for (k,v) in globals().iteritems() if k in list_of_variable_names)
или же
dict((k,v) for (k,v) in vars().iteritems() if k in list_of_variable_names)
в зависимости от того, что вы хотите.
Забудьте о фильтрации locals()
! Словарь, который вы передаете строке форматирования, может содержать неиспользуемые ключи:
>>> name = 'foo'
>>> zip = 123
>>> unused = 'whoops!'
>>> locals()
{'name': 'foo', 'zip': 123, ... 'unused': 'whoops!', ...}
>>> '%(name)s %(zip)i' % locals()
'foo 123'
С новым функция f-string в Python 3.6 использование locals()
больше не требуется:
>>> name = 'foo'
>>> zip = 123
>>> unused = 'whoops!'
>>> f'{zip: >5} {name.upper()}'
' 123 FOO'
и если вы используете метод str.format (), сделайте это так: '{name} {zip}'.format(**locals())
Моддинг вниз. Я не вижу разумной ситуации, в которой вы бы хотели это сделать, и я не вижу способа сделать это надежным, и уж тем более с eval. Что делать, если имя списка имен скрывает одно из интересующих имен и т. д.