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

2025-03-19 08:56:00
admin
原创
59
摘要:问题描述:我想使用函数中定义的一组局部变量,在函数之外。所以我传递了x=locals()返回值。我怎样才能将该字典中定义的所有变量加载到函数外部的命名空间中,这样我就不用使用 来访问值x['variable'],而只需使用 即可variable。解决方案 1:您不需要创建自己的对象,而是可以使用argpars...

问题描述:

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

我怎样才能将该字典中定义的所有变量加载到函数外部的命名空间中,这样我就不用使用 来访问值x['variable'],而只需使用 即可variable


解决方案 1:

您不需要创建自己的对象,而是可以使用argparse.Namespace

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

执行相反的操作:

mydict = vars(ns)

解决方案 2:

考虑一下Bunch替代方案:

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

因此,如果你有一本字典d,并且想要使用语法x.foo而不是笨拙的语法来访问(读取)它的值d['foo'],那么只需这样做

x = Bunch(d)

这在函数内部和外部都有效——而且比注入干净、更安全!记住 Python 之禅的最后一句话……:d`globals()`

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

解决方案 3:

只要知道自己在做什么,将一个局部空间中的变量导入另一个局部空间是完全有效的。我多次看到这样的代码被以有用的方式使用。只需小心不要污染公共全局空间。

您可以执行以下操作:

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

解决方案 4:

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

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

return locals()

然后导入如下:

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

解决方案 5:

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方法- 因为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 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)

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)请注意,后者不是递归的,但仍然可以与其他类进行比较,因为子级结构无论如何都可以很好地比较)。

解决方案 6:

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

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

解决方案 7:

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

解决方案 8:

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

for key, val in x.items():  # unpack the keys from the dictionary to individual variables
    exec (key + '=val')
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2482  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1533  
  PLM(产品生命周期管理)项目对于企业优化产品研发流程、提升产品质量以及增强市场竞争力具有至关重要的意义。然而,在项目推进过程中,范围蔓延是一个常见且棘手的问题,它可能导致项目进度延迟、成本超支以及质量下降等一系列不良后果。因此,有效避免PLM项目范围蔓延成为项目成功的关键因素之一。以下将详细阐述三大管控策略,助力企业...
plm系统   0  
  PLM(产品生命周期管理)项目管理在企业产品研发与管理过程中扮演着至关重要的角色。随着市场竞争的加剧和产品复杂度的提升,PLM项目面临着诸多风险。准确量化风险优先级并采取有效措施应对,是确保项目成功的关键。五维评估矩阵作为一种有效的风险评估工具,能帮助项目管理者全面、系统地评估风险,为决策提供有力支持。五维评估矩阵概述...
免费plm软件   0  
  引言PLM(产品生命周期管理)开发流程对于企业产品的全生命周期管控至关重要。它涵盖了从产品概念设计到退役的各个阶段,直接影响着产品质量、开发周期以及企业的市场竞争力。在当今快速发展的科技环境下,客户对产品质量的要求日益提高,市场竞争也愈发激烈,这就使得优化PLM开发流程成为企业的必然选择。缺陷管理工具和六西格玛方法作为...
plm产品全生命周期管理   0  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用