I want to use a bunch of local variables defined in a function, outside of the function. So I am passing x=locals()
in the return value.
How can I load
Rather than create your own object, you can use argparse.Namespace
:
from argparse import Namespace
ns = Namespace(**mydict)
To do the inverse:
mydict = vars(ns)
Used following snippet (PY2) to make recursive namespace from my dict(yaml) configs:
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)
The Bunch answer is ok but lacks recursion and proper __repr__
and __eq__
builtins to simulate what you can already do with a dict. Also the key to recursion is not only to recurse on dicts but also on lists, so that dicts inside lists are also converted.
These two options I hope will cover your needs (you might have to adjust the type checks in __elt()
for more complex objects; these were tested mainly on json imports so very simple core types).
repr(obj)
will return Bunch({...})
that can be re-interpreted into an equivalent object.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__
__repr__
and __eq__
, all you need is to implement a recursive __init__
method: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
The RecursiveNamespace class takes keyword arguments, which can of course come from a de-referenced dict (ex **mydict
)
Now lets put them to the test:
adict = {'foo': 'bar', 'baz': [{'aaa': 'bbb', 'ccc': 'ffffd'}]}
a = Bunch(adict)
b = RecursiveNamespace(**adict)
print('a:', str(a))
print('b:', str(b))
print('a == b :', str(a == b))
The result is:
a: Bunch({'foo': 'bar', 'baz': [Bunch({'aaa': 'bbb', 'ccc': 'ffffd'})]})
b: RecursiveNamespace(baz=[RecursiveNamespace(aaa='bbb', ccc='ffffd')], foo='bar')
a == b : True
Although those are different classes, because both have been initialized to equivalent namespaces and their __eq__
method compares the namespace only (self.__dict__
), comparing the two namespace objects returns True
You might also notice that I recurse using type(self)(...)
rather than using the class name - this has two advantages: first the class can be renamed without having to update recursive calls, and second if the class is subclassed we'll be recursing using the subclass name. It's also the name used in __repr__
(type(self).__name__
).
There's Always this option, I don't know that it is the best method out there, but it sure does work. Assuming type(x) = dict
for key, val in x.items(): # unpack the keys from the dictionary to individual variables
exec (key + '=val')
Consider the Bunch
alternative:
class Bunch(object):
def __init__(self, adict):
self.__dict__.update(adict)
so if you have a dictionary d
and want to access (read) its values with the syntax x.foo
instead of the clumsier d['foo']
, just do
x = Bunch(d)
this works both inside and outside functions -- and it's enormously cleaner and safer than injecting d
into globals()
! Remember the last line from the Zen of Python...:
>>> import this
The Zen of Python, by Tim Peters
...
Namespaces are one honking great idea -- let's do more of those!
Importing variables into a local namespace is a valid problem and often utilized in templating frameworks.
Return all local variables from a function:
return locals()
Then import as follows:
r = fce()
for key in r.keys():
exec(key + " = r['" + key + "']")