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


72

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

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


10
这听起来像一个可怕的主意。
约翰·拉鲁伊

5
比“ from module import *”的概念要可怕,因为大概您对dict中的内容有了更多的了解。
Binary Phile 2012年

4
@JohnLaRooy为什么这是一个可怕的主意?
查理·帕克

Answers:


99

考虑Bunch替代方案:

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

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

x = Bunch(d)

这个作品内外的功能-它的巨大清洁,较安全的注射dglobals()!记住Python Zen中的最后一行...:

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

14
但这看起来很优雅:globals().update(locals())!(我
开玩笑

1
我在简单的绘图脚本中使用了globals()。update(locals()),在该脚本中,当以交互方式加载模块或以一种单一方式从命令行运行时,我会有一些简单的区别。我想我可以将if name ==' main ':放在文件的顶部,然后不具有函数...但这似乎毫无意义。无论如何,如果有更好的方法将值从函数内部传递到外部模块,我会很感兴趣。
mathtick

喜欢这个,但是它不是递归的。嵌套字典有什么解决方案?
Nomen Nescio '18

@CleverGuy您可以递归地编写它,只要您不使用update:而是k,vadict.items()和配对if type(v) is dict: self.__dict__[k]=Bunch(v),然后else: self.__dict__[k]=v进行其他所有操作。您可以像命名空间一样访问结果,包括为嵌套空间分配新值。
粘土

53

除了创建自己的对象,还可以使用argparse.Namespace

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

要做相反的事情:

mydict = vars(ns)

2
最后,一些安全,易读且已在标准库中实现的东西。我不知道为什么它honking great idea驻留在argparse模块中。
亚瑟·卡兹布斯

2
现在可作为types.SimpleNamespace
PaulMcG

这是一个超级解决方案,值得赞扬。请参阅repl.it/repls/NavyFlusteredSearchengine,以了解它有多简单
Mike Crowe

“逆”部分有一个细微的警告:mydict = vars(ns)新字典和原始名称空间同时执行时,将指向同一个对象(更改任何一个也将更改另一个)。这样做mydict = vars(ns).copy(),以避免这种情况。
Aurelio Jargas

16

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

您可以执行以下操作:

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

def func(x):locals()。update(x)print(pos)print(locals())func({“ pos”:23})---------------- -------------------------------------------------- --------- NameError追溯(最近一次呼叫最近)<ipython-input-211-4eae42dbc044>在<module>()中3 print(pos)4 print(locals())----> 5 func({“ pos”:23})在func(x)中的<ipython-input-211-4eae42dbc044> 1 def func(x):2 locals()。update(x)----> 3 print(pos) 4 print(locals())5 func({“ pos”:23})NameError:未定义名称“ pos”
O.rka


5

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

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

return locals()

然后导入如下:

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

2
可以正常运行,但是速度很慢……对于简单的测试程序来说,这不是问题。
snowcrash09年

1

答案是确定的,但缺乏递归和适当的__repr__,并__eq__内建模拟你已经可以做一个字典。同样,递归的关键不仅在于对字典进行递归,而且还对列表进行递归,因此列表内的字典也将被转换。

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

  1. 方法(按照前文的回答) -对象采取字典和递归地将其转换。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__)中使用的名称。


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)

-1

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

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

这很好用,直到实习生抓住你,并认为“ exec”是自Python以来最好的东西。并且以防万一有实习生在读这本书,请不要这样做。这会损害代码的可读性,使其难以调试,运行速度异常慢,并且可能会导致很多我什至无法正确预期和解释的问题。
雪松

如果沿着这条路线走,只需直接更新本地人即可。例如locals().update(x)
BrianHVB
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.