我想在函数外部使用一堆在函数中定义的局部变量。因此,我传递x=locals()
了返回值。
如何将该词典中定义的所有变量加载到函数外部的名称空间中,这样x['variable']
我可以简单地使用而不是使用来访问值variable
。
我想在函数外部使用一堆在函数中定义的局部变量。因此,我传递x=locals()
了返回值。
如何将该词典中定义的所有变量加载到函数外部的名称空间中,这样x['variable']
我可以简单地使用而不是使用来访问值variable
。
Answers:
考虑Bunch
替代方案:
class Bunch(object):
def __init__(self, adict):
self.__dict__.update(adict)
因此,如果您有一本字典,d
并且想要使用语法x.foo
而不是clumsier来访问(读取)其值d['foo']
,请执行
x = Bunch(d)
这个作品内外的功能-它的巨大清洁,较安全的注射d
进globals()
!记住Python Zen中的最后一行...:
>>> import this
The Zen of Python, by Tim Peters
...
Namespaces are one honking great idea -- let's do more of those!
globals().update(locals())
!(我
update
:而是k,v
与adict.items()
和配对if type(v) is dict: self.__dict__[k]=Bunch(v)
,然后else: self.__dict__[k]=v
进行其他所有操作。您可以像命名空间一样访问结果,包括为嵌套空间分配新值。
除了创建自己的对象,还可以使用argparse.Namespace
:
from argparse import Namespace
ns = Namespace(**mydict)
要做相反的事情:
mydict = vars(ns)
honking great idea
驻留在argparse
模块中。
types.SimpleNamespace
mydict = vars(ns)
新字典和原始名称空间同时执行时,将指向同一个对象(更改任何一个也将更改另一个)。这样做mydict = vars(ns).copy()
,以避免这种情况。
只要一个人知道他/她在做什么,这是将一个本地空间中的变量导入另一个本地空间的完全有效的情况。我已经多次看到这种代码以有用的方式使用。只需要注意不要污染共同的全球空间。
您可以执行以下操作:
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 + "']")
该束答案是确定的,但缺乏递归和适当的__repr__
,并__eq__
内建模拟你已经可以做一个字典。同样,递归的关键不仅在于对字典进行递归,而且还对列表进行递归,因此列表内的字典也将被转换。
我希望这两个选项可以满足您的需求(您可能需要__elt()
针对更复杂的对象调整类型检查;它们主要在json导入中进行了测试,因此核心类型非常简单)。
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__
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')
locals().update(x)
。