Python: load variables in a dict into namespace

前端 未结 7 734
天命终不由人
天命终不由人 2020-11-27 12:15

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

相关标签:
7条回答
  • 2020-11-27 12:46

    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)
    
    0 讨论(0)
  • 2020-11-27 12:49

    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)
    
    0 讨论(0)
  • 2020-11-27 12:53

    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).

    1. The Bunch approach (as per previous answer) - object takes a dict and converts it recursively. 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__
    
    1. The SimpleNamespace approach - since types.SimpleNamespace already implements __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__).

    0 讨论(0)
  • 2020-11-27 12:56

    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')
    
    0 讨论(0)
  • 2020-11-27 12:58

    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!
    
    0 讨论(0)
  • 2020-11-27 13:01

    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 + "']")
    
    0 讨论(0)
提交回复
热议问题