Каков наилучший способ превратить словарь Python в переменные?
У нас есть config.yaml
файл как это:
darwin:
installer:
title: "%(product_name)s %(version)s"
filename: "%(brand_name)s-%(version)s"
и функция для его форматирования:
def format_context(config):
return {
"company_name": config['company_name'],
"product_name": config['product_name'],
"brand_name": config['brand_name'],
"title": config['darwin']['installer']['title'],
"filename": config['darwin']['installer']['filename'],
}
Цель здесь - мы можем ввести значение в виде отформатированной строки. Теперь мне нужно включить словарь возврата по format_context
в переменные.
Первая попытка использования locals()
:
context = format_context(config)
for k, v in context.iteritems():
locals()[k] = str(v) % context
Но, возможно, из-за заказа я иногда получал KeyError
ошибка. И более того, из документа Python:
Примечание. Содержимое этого словаря не должно изменяться; изменения могут не повлиять на значения локальных и свободных переменных, используемых интерпретатором.
Итак, я перешел на использование exec
:
context = format_context(config)
for k, v in context.iteritems():
exec("%s = '%s'" % (k, str(v) % context))
Это работает, но мне интересно, это хороший способ?
Пожалуйста, позвольте мне уточнить, почему я собираюсь создавать переменные из этого диктата. У меня есть функция, чтобы разобрать это config.yaml
:
class BrandConfiguration(object):
"""
A brand configuration (directory)
"""
def __init__(self, directory):
self.dirname = directory
@property
def config(self):
"""
return configuration for a single brand
"""
with open(os.path.join(self.dirname, "config.yaml")) as fh:
return yaml.load(fh)
Затем в одном классе я определил несколько переменных:
- brand_config = self.brand_config_instance.config
- binary_name = brand_config['binary_name']
- major_version = brand_config['version']['major']
- minor_version = brand_config['version']['minor']
- patch_version = brand_config['version']['patch']
В другом классе (или другом файле Python) мне нужно сделать то же самое:
- brand_name, binary_name = config['brand_name'], config['binary_name']
- identifiers = [binary_name] + brand_name.split('.')
- identifiers.reverse()
- identifier = '.'.join(identifiers)
- major_version = config['version']['major']
- minor_version = config['version']['minor']
- patch_version = config['version']['patch']
- version = '.'.join(
- (
- str(major_version),
- str(minor_version),
- str(patch_version),
- build_number
- )
- )
Поскольку я не хочу дублировать код, я пытаюсь сохранить все это в словаре и преобразовать в переменные.
Где / как вы пытаетесь использовать значения из словаря, возвращаемого format_context?
Предполагая, что в config.yaml
, у вас есть что-то вроде этого:
version:
major: 1
minor: 0
patch: 0
При добавлении метаданных для Windows вместо создания некоторых переменных:
- brand_name, binary_name = config['brand_name'], config['binary_name']
- identifiers = [binary_name] + brand_name.split('.')
- identifiers.reverse()
- identifier = '.'.join(identifiers)
- major_version = config['version']['major']
- minor_version = config['version']['minor']
- patch_version = config['version']['patch']
- version = '.'.join(
- (
- str(major_version),
- str(minor_version),
- str(patch_version),
- build_number
- )
- )
Теперь я могу использовать его напрямую:
json_data['FixedFileInfo']['FileVersion']['Major'] = major_version
json_data['FixedFileInfo']['FileVersion']['Minor'] = minor_version
json_data['FixedFileInfo']['FileVersion']['Patch'] = patch_version
json_data['FixedFileInfo']['FileVersion']['Build'] = build_number
json_data['FixedFileInfo']['ProductVersion'] = \
json_data['FixedFileInfo']['FileVersion']
json_data['StringFileInfo']['CompanyName'] = company_name
json_data['StringFileInfo']['FileDescription'] = service_description
json_data['StringFileInfo']['LegalCopyright'] = legal_copyright
json_data['StringFileInfo']['ProductName'] = product_name
json_data['StringFileInfo']['ProductVersion'] = '.'.join(
(
str(major_version),
str(minor_version),
str(patch_version),
self.target.build_number
)
)
1 ответ
argparse
пользователи иногда спрашивают о преобразовании args
атрибуты в глобальные или местные жители, например
Python argparse parse_args в глобальное пространство имен (или причина, по которой это плохая идея)
Python Argparse, как ссылаться на аргументы по имени
argparse
возвращает проанализированные аргументы как Namespace
объект, который определяется с помощью этого кода:
In [245]: argparse.Namespace??
Init signature: argparse.Namespace(**kwargs)
Source:
class Namespace(_AttributeHolder):
"""Simple object for storing attributes.
Implements equality by attribute names and values, and provides a simple
string representation.
"""
def __init__(self, **kwargs):
for name in kwargs:
setattr(self, name, kwargs[name])
def __eq__(self, other):
if not isinstance(other, Namespace):
return NotImplemented
return vars(self) == vars(other)
def __contains__(self, key):
return key in self.__dict__
File: /usr/lib/python3.5/argparse.py
Type: type
Такой объект может быть создан из словаря с
In [247]: adict={'a':1,'b':'aname','c':[1,2,3]}
In [248]: ns=argparse.Namespace(**adict)
In [249]: ns
Out[249]: Namespace(a=1, b='aname', c=[1, 2, 3])
И атрибуты могут быть доступны по имени:
In [250]: ns.a
Out[250]: 1
In [251]: ns.b
Out[251]: 'aname'
In [252]: ns.c
Out[252]: [1, 2, 3]
Этот синтаксис похож на запрос переменной a
в модуле с именем ns
,
Это может быть легко превращено обратно в словарь с vars
:
In [253]: vars(ns)
Out[253]: {'a': 1, 'b': 'aname', 'c': [1, 2, 3]}
Обратите внимание, что Namespace
устанавливает атрибуты с setattr
, Это более мощный, чем синтаксис `ns.abc='123', поскольку он создает атрибуты, которые не являются допустимыми именами переменных:
In [254]: setattr(ns,'123','foo')
In [255]: ns
Out[255]: Namespace(123='foo', a=1, b='aname', c=[1, 2, 3])
In [256]: ns.123
File "<ipython-input-256-f3ac9938f9b1>", line 1
ns.123
^
SyntaxError: invalid syntax
In [257]: getattr(ns,'123')
Out[257]: 'foo'
Некоторые программы, такие как Ipython
загрузить аргументы из config
файл (ы), а затем использовать argparse
читать аргументы в последнюю минуту из командной строки, предоставляя пользователям несколько способов настройки параметров. Обычно более полезно хранить эти параметры независимо от источника, собранные в одном месте (пространство имен или словарь), а не объединенные в globals
или же locals
,