Каков наилучший способ превратить словарь 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,

Другие вопросы по тегам