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

发布于 2024-08-27 17:23:49 字数 186 浏览 6 评论 0原文

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

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

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 all the variables defined in that dictionary into the namespace outside the function, so that instead of accessing the value using x['variable'], I could simply use variable.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

樱花坊 2024-09-03 17:23:49

您可以使用 argparse.Namespace 来代替创建自己的对象:

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

执行相反的操作:

mydict = vars(ns)

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)
唠甜嗑 2024-09-03 17:23:49

考虑使用 Bunch 替代方案:

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

因此,如果您有一个字典 d 并且希望使用语法 x.foo 访问(读取)其值,而不是笨拙的 d['foo'],只需在

x = Bunch(d)

函数内部和外部执行此操作即可 - 而且它比注入 d 更干净、更安全进入globals()!记住 Python 之禅的最后一行……:

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

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!
疯到世界奔溃 2024-09-03 17:23:49

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

您可以执行以下操作:

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

This is perfectly valid case to import variables in
one local space into another local space as long as
one is aware of what he/she is doing.
I have seen such code many times being used in useful ways.
Just need to be careful not to pollute common global space.

You can do the following:

adict = { 'x' : 'I am x', 'y' : ' I am y' }
locals().update(adict)
blah(x)
blah(y)
夜吻♂芭芘 2024-09-03 17:23:49

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

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

return locals()

然后按如下方式导入:

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

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 + "']")
唯憾梦倾城 2024-09-03 17:23:49

Bunch 答案是好的,但缺乏递归和适当的 __repr____eq__ 内置函数来模拟你已经可以用字典做的事情。另外,递归的关键不仅是对字典进行递归,还要对列表进行递归,这样列表内的字典也会被转换。

我希望这两个选项能够满足您的需求(对于更复杂的对象,您可能需要调整 __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 方法 - 由于类型.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)

< em>RecursiveNamespace 类采用关键字参数,当然可以来自取消引用的字典(例如 **mydict


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

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__ 方法(已注释掉),以允许与原始 dictargparse.Namespace(**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).

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

The RecursiveNamespace class takes keyword arguments, which can of course come from a de-referenced dict (ex **mydict)


Now let's put them to the test (argparse.Namespace added for comparison, although it's nested dict is manually converted):

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

The result is:

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

Although those are different classes, because they both (a and b) have been initialized to equivalent namespaces and their __eq__ method compares the namespace only (self.__dict__), comparing two namespace objects returns True. For the case of comparing with argparse.Namespace, for some reason only Bunch works and I'm unsure why (please comment if you know, I haven't looked much further as types.SimpleNameSpace is a built-in implementation).

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

EDIT 2021-11-27:

  1. Modified the Bunch.__eq__ method to make it safe against type mismatch.

  2. Added/modified optional __eq__ methods (commented out) to allow comparing with the original dict and argparse.Namespace(**dict) (note that the later is not recursive but would still be comparable with other classes as the sublevel structs would compare fine anyway).

梅倚清风 2024-09-03 17:23:49

为了扩展 Thomas 的想法,以下内容保留了任何支持列表到类型转换的可迭代对象的类型。

import types
import sys

class RecursiveNamespace(types.SimpleNamespace):
    def __init__(self, accepted_iter_types = [], **kwargs):
        self.supported_types = [list, tuple, set] + accepted_iter_types

        for key, val in kwargs.items():
            if type(val) == dict:
                setattr(self, key, RecursiveNamespace(**val))
            elif hasattr(val, '__iter__'): # object is iterable
                setattr(self, key, self.__make_iterable(val))
            else: 
                setattr(self, key, val)

    def __make_iterable(self, val):
        if(type(val) not in self.supported_types): # not a supoorted iterable type
            return val
        lst = [self.__recurse_in(v) for v in val]
        try:
            ret = type(val)(lst)  # the type is assumed to support list-to-type conversion
        except Exception as e:
            print(f"Failed to make iterable object of type {type(val)}", e, out=sys.stderr)
        return  ret 

    def __recurse_in(self, val):
        if type(val) == dict:
            return RecursiveNamespace(**val)
        elif(hasattr(val, '__iter__')): # if it's iterable
            return self.__make_iterable(val)        
        else:
            return val

    def __getitem__(self, key):
        return self.__dict__[key]

if __name__ == '__main__':
    data = {'a': 1, 
        'b': (2,3), 
        'c': [4,5],
        'd': set([6,'7',8]),
        'e': {
            'e_1': 9, 
            'e_2': {
                    'e_2_1': 10, 
                    'e_2_2': (11,)
                    },
            'e_3': [12,13]}
    }

    rname = RecursiveNamespace(**data)
    print(rname)
    print('%20s :'%(type(rname.a)), rname.a)
    print('%20s :'%(type(rname.b)), rname.b)
    print('%20s :'%(type(rname.c)), rname.c)
    print('%20s :'%(type(rname.d)), rname.d)
    print('%20s :'%(type(rname.e.e_2.e_2_2)), rname.e.e_2.e_2_2)

输出:

   <class 'int'> : 1
 <class 'tuple'> : (2, 3)
  <class 'list'> : [4, 5]
   <class 'set'> : {8, '7', 6}
 <class 'tuple'> : (11,)

To extend Thomas's idea, the following preserves type of any supported iterable object that supports list-to-type conversion.

import types
import sys

class RecursiveNamespace(types.SimpleNamespace):
    def __init__(self, accepted_iter_types = [], **kwargs):
        self.supported_types = [list, tuple, set] + accepted_iter_types

        for key, val in kwargs.items():
            if type(val) == dict:
                setattr(self, key, RecursiveNamespace(**val))
            elif hasattr(val, '__iter__'): # object is iterable
                setattr(self, key, self.__make_iterable(val))
            else: 
                setattr(self, key, val)

    def __make_iterable(self, val):
        if(type(val) not in self.supported_types): # not a supoorted iterable type
            return val
        lst = [self.__recurse_in(v) for v in val]
        try:
            ret = type(val)(lst)  # the type is assumed to support list-to-type conversion
        except Exception as e:
            print(f"Failed to make iterable object of type {type(val)}", e, out=sys.stderr)
        return  ret 

    def __recurse_in(self, val):
        if type(val) == dict:
            return RecursiveNamespace(**val)
        elif(hasattr(val, '__iter__')): # if it's iterable
            return self.__make_iterable(val)        
        else:
            return val

    def __getitem__(self, key):
        return self.__dict__[key]

if __name__ == '__main__':
    data = {'a': 1, 
        'b': (2,3), 
        'c': [4,5],
        'd': set([6,'7',8]),
        'e': {
            'e_1': 9, 
            'e_2': {
                    'e_2_1': 10, 
                    'e_2_2': (11,)
                    },
            'e_3': [12,13]}
    }

    rname = RecursiveNamespace(**data)
    print(rname)
    print('%20s :'%(type(rname.a)), rname.a)
    print('%20s :'%(type(rname.b)), rname.b)
    print('%20s :'%(type(rname.c)), rname.c)
    print('%20s :'%(type(rname.d)), rname.d)
    print('%20s :'%(type(rname.e.e_2.e_2_2)), rname.e.e_2.e_2_2)

output:

   <class 'int'> : 1
 <class 'tuple'> : (2, 3)
  <class 'list'> : [4, 5]
   <class 'set'> : {8, '7', 6}
 <class 'tuple'> : (11,)
謌踐踏愛綪 2024-09-03 17:23:49

使用以下代码片段 (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)

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)
霓裳挽歌倾城醉 2024-09-03 17:23:49

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

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

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')
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文