86

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

如何将该字典中定义的所有变量加载到函数外部的命名空间中,这样x['variable']我就可以简单地使用variable.

4

7 回答 7

104

考虑Bunch替代方案:

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

因此,如果您有一本字典d并想使用语法x.foo而不是 clumsier访问(读取)其值d['foo'],只需执行

x = Bunch(d)

这在内部和外部功能都有效 - 它比注入更清洁和安全!记住 Python 之禅的最后一行...:dglobals()

>>> import this
The Zen of Python, by Tim Peters
   ...
Namespaces are one honking great idea -- let's do more of those!
于 2010-04-08T03:40:50.110 回答
92

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

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

做相反的事情:

mydict = vars(ns)
于 2018-05-01T14:54:58.807 回答
18

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

您可以执行以下操作:

adict = { 'x' : 'I am x', 'y' : ' I am y' }
locals().update(adict)
blah(x)
blah(y)
于 2010-10-25T11:24:40.450 回答
6

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

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

return locals()

然后导入如下:

r = fce()
for key in r.keys():
   exec(key + " = r['" + key + "']")
于 2011-02-05T10:20:25.583 回答
1

Bunch的答案还可以,但缺少递归和适当的__repr__内置__eq__函数来模拟您已经可以使用 dict 执行的操作。此外,递归的关键不仅是对字典进行递归,而且对列表进行递归,以便列表内的字典也被转换。

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

  1. Bunch方法(根据先前的答案) - 对象接受一个字典并递归地转换它。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 namespace 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):
        if hasattr(other, '__dict__'):
            return self.__dict__ == other.__dict__
        return NotImplemented
        # Use this to allow comparing with dicts:
        #return self.__dict__ == (other.__dict__ if hasattr(other, '__dict__') else other)
  1. SimpleNamespace方法——因为已经实现了and ,所以你需要实现一个递归方法: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 namespace 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

    # Optional, allow comparison with dicts:
    #def __eq__(self, other):
    #    return self.__dict__ == (other.__dict__ if hasattr(other, '__dict__') else other)

RecursiveNamespace类接受关键字参数,它当然可以来自取消引用的 dict (ex **mydict)


现在让我们对它们进行测试(argparse.Namespace添加用于比较,尽管它的嵌套 dict 是手动转换的):

from argparse import Namespace
from itertools import combinations
adict = {'foo': 'bar', 'baz': [{'aaa': 'bbb', 'ccc': 'ddd'}]}
a = Bunch(adict)
b = RecursiveNamespace(**adict)
c = Namespace(**adict)
c.baz[0] = Namespace(**c.baz[0])
for n in ['a', 'b', 'c']:
    print(f'{n}:', str(globals()[n]))
for na, nb in combinations(['a', 'b', 'c'], 2):
    print(f'{na} == {nb}:', str(globals()[na] == globals()[nb]))

结果是:

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

尽管它们是不同的类,因为它们 (ab) 都已初始化为等效的命名空间,并且它们的__eq__方法仅比较命名空间 ( self.__dict__),比较两个命名空间对象返回True. 对于与 进行比较的情况argparse.Namespace,由于某种原因只能Bunch工作,我不确定为什么(如果你知道,请发表评论,我没有像types.SimpleNameSpace内置实现那样进一步研究)。

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

编辑 2021-11-27:

  1. 修改了Bunch.__eq__方法以防止类型不匹配。

  2. 添加/修改了可选__eq__方法(注释掉)以允许与原始dict和比较argparse.Namespace(**dict)(请注意,后者不是递归的,但仍然可以与其他类进行比较,因为子级结构无论如何都会比较好)。

于 2020-05-29T04:19:27.820 回答
0

使用以下代码段 (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)
于 2018-07-10T11:44:27.817 回答
-2

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

for key, val in x.items():  # unpack the keys from the dictionary to individual variables
    exec (key + '=val')
于 2016-09-16T20:35:15.140 回答