将Python字典转换为变量的最佳方法是什么?

3
我们有一个类似于这样的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
        )
    )

4
我不明白你为什么要创建变量,以及YAML文档与你问题的其他部分有什么关系。如果你想替换其中的%(...)s表达式,只需将formatted_yaml = raw_yaml % format_context(config)设置即可。 - larsks
我需要从config.yaml文件中设置一些常见变量,而且我不想重复代码。我更新了我的原始问题以澄清。现在更清楚了吗? - quanta
我仍然感到困惑,因为你的 BrandConfiguration 类似乎没有进行任何模板化。你在哪里/如何使用 format_context 返回的字典中的值?我强烈怀疑在这里使用 locals() 是错误的解决方案,但由于我不清楚你想要实现什么,所以我不确定能否提供更好的解决方案。 - larsks
那么我需要在另一个文件中为其他操作系统做同样的事情。--- 在另一个Python文件中吗?如果代码相同,只需在公共实用程序模块中使用一个函数或在基类中使用一个方法,该基类由特定于操作系统的子类继承。如果数据结构“对所有操作系统都相同”,我不明白为什么需要重复解释它的代码。 - Kevin J. Chase
1
如果你需要在一个函数中使用这些变量,只需通过 **kwargs 将字典传递进去即可。 - hpaulj
显示剩余6条评论
1个回答

0

argparse的用户有时会询问如何将args属性转换为全局或局部变量,例如:

Python argparse parse_args into global namespace (or a reason this is a bad idea)

python argparse, how to refer args by their name

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]

这个语法就像是在一个名为ns的模块中请求变量a

可以使用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 从命令行读取最后一分钟的参数,为用户提供了几种设置选项的方式。通常更有用的是将这些选项(无论来源如何)收集在一个地方(名称空间或字典)而不是合并到 globalslocals 中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接