繁体   English   中英

Python:将字典中的变量加载到命名空间中

[英]Python: load variables in a dict into namespace

我想在函数之外使用一堆在函数中定义的局部变量。 所以我在返回值中传递x=locals()

如何将该字典中定义的所有变量加载到函数外部的命名空间中,以便我可以简单地使用variable而不是使用x['variable']访问值。

考虑Bunch替代方案:

class Bunch(object):
  def __init__(self, adict):
    self.__dict__.update(adict)

因此,如果您有一个字典d并且想要使用语法x.foo而不是笨拙的d['foo']来访问(读取)它的值,只需执行

x = Bunch(d)

这个作品内外的功能-它的巨大清洁和更安全比注射dglobals() 记住 Python 之禅的最后一行...:

>>> import this
The Zen of Python, by Tim Peters
   ...
Namespaces are one honking great idea -- let's do more of those!

您可以使用argparse.Namespace ,而不是创建自己的对象:

from argparse import Namespace
ns = Namespace(**mydict)

做相反的事情:

mydict = vars(ns)

这是将一个本地空间中的变量导入另一个本地空间的完全有效的情况,只要人们知道他/她在做什么。 我已经多次看到这样的代码以有用的方式被使用。 只需要注意不要污染共同的全球空间。

您可以执行以下操作:

adict = { 'x' : 'I am x', 'y' : ' I am y' }
locals().update(adict)
blah(x)
blah(y)

将变量导入本地命名空间是一个有效的问题,经常在模板框架中使用。

从函数返回所有局部变量:

return locals()

然后导入如下:

r = fce()
for key in r.keys():
   exec(key + " = r['" + key + "']")

Bunch 的答案是可以的,但缺乏递归和适当的__repr____eq__内置__eq__来模拟你已经可以用 dict 做的事情。 此外,递归的关键不仅是在字典上递归,而且在列表上递归,这样列表中的字典也会被转换。

我希望这两个选项能够满足您的需求(您可能需要为更复杂的对象调整__elt()的类型检查;这些主要在 json 导入上进行了测试,因此非常简单的核心类型)。

  1. Bunch方法(根据之前的答案)- object 接受一个 dict 并递归地转换它。 repr(obj)将返回可以重新解释为等效对象的Bunch({...})
class Bunch(object):
    def __init__(self, adict):
        """Create a namespace object from a dict, recursively"""
        self.__dict__.update({k: self.__elt(v) for k, v in adict.items()})

    def __elt(self, elt):
        """Recurse into elt to create leaf namepace objects"""
        if type(elt) is dict:
            return type(self)(elt)
        if type(elt) in (list, tuple):
            return [self.__elt(i) for i in elt]
        return elt

    def __repr__(self):
        """Return repr(self)."""
        return "%s(%s)" % (type(self).__name__, repr(self.__dict__))

    def __eq__(self, other):
        return self.__dict__ == other.__dict__
  1. SimpleNamespace方法 - 因为types.SimpleNamespace已经实现了__repr____eq__ ,所以你需要的只是实现一个递归的__init__方法:
import types
class RecursiveNamespace(types.SimpleNamespace):
    # def __init__(self, /, **kwargs):  # better, but Python 3.8+
    def __init__(self, **kwargs):
        """Create a SimpleNamespace recursively"""
        self.__dict__.update({k: self.__elt(v) for k, v in kwargs.items()})

    def __elt(self, elt):
        """Recurse into elt to create leaf namepace objects"""
        if type(elt) is dict:
            return type(self)(**elt)
        if type(elt) in (list, tuple):
            return [self.__elt(i) for i in elt]
        return elt

RecursiveNamespace类采用关键字参数,这当然可以来自取消引用的字典(例如**mydict


现在让我们来测试一下:

adict = {'foo': 'bar', 'baz': [{'aaa': 'bbb', 'ccc': 'ddd'}]}
a = Bunch(adict)
b = RecursiveNamespace(**adict)
print('a:', str(a))
print('b:', str(b))
print('a == b :', str(a == b))

结果是:

a: Bunch({'foo': 'bar', 'baz': [Bunch({'aaa': 'bbb', 'ccc': 'ddd'})]})
b: RecursiveNamespace(baz=[RecursiveNamespace(aaa='bbb', ccc='ddd')], foo='bar')
a == b : True

尽管它们是不同的类,因为它们都已初始化为等效的命名空间,并且它们的__eq__方法仅比较命名空间( self.__dict__ ),因此比较两个命名空间对象返回True

您可能还注意到我使用type(self)(...)而不是使用类名进行递归 - 这有两个优点:首先可以重命名类而不必更新递归调用,其次如果类是子类,我们'将使用子类名称递归。 它也是__repr__ ( type(self).__name__ ) 中使用的名称。

使用以下代码段 (PY2) 从我的 dict(yaml) 配置中创建递归命名空间:

class NameSpace(object):
    def __setattr__(self, key, value):
        raise AttributeError('Please don\'t modify config dict')


def dump_to_namespace(ns, d):
    for k, v in d.iteritems():
        if isinstance(v, dict):
            leaf_ns = NameSpace()
            ns.__dict__[k] = leaf_ns
            dump_to_namespace(leaf_ns, v)
        else:
            ns.__dict__[k] = v

config = NameSpace()
dump_to_namespace(config, config_dict)

总是有这个选项,我不知道这是最好的方法,但它确实有效。 假设 type(x) = dict

for key, val in x.items():  # unpack the keys from the dictionary to individual variables
    exec (key + '=val')

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM