解构绑定字典内容

2025-03-18 08:55:00
admin
原创
55
摘要:问题描述:我正在尝试“解构”一本字典,并将值与其键后的变量名称关联起来。例如params = {'a':1,'b':2} a,b = params.values() 但是由于字典是无序的,因此无法保证params.values()将按 的顺序返回值(a, b)。 有没有什么好的方法可以做到这一点?解决方案 1...

问题描述:

我正在尝试“解构”一本字典,并将值与其键后的变量名称关联起来。例如

params = {'a':1,'b':2}
a,b = params.values()

但是由于字典是无序的,因此无法保证params.values()将按 的顺序返回值(a, b)。 有没有什么好的方法可以做到这一点?


解决方案 1:

from operator import itemgetter

params = {'a': 1, 'b': 2}

a, b = itemgetter('a', 'b')(params)

除了复杂的 lambda 函数或字典理解之外,还不如使用内置库。

解决方案 2:

怎么没有人发布最简单的方法?

params = {'a':1,'b':2}

a, b = params['a'], params['b']

解决方案 3:

有一种比 Jochen 的建议更少重复的方法,那就是使用辅助函数。这可以灵活地以任何顺序列出变量名,并且只解构字典中的一部分内容:

pluck = lambda dict, *args: (dict.get(arg, -1) for arg in args)

things = {'blah': 'bleh', 'foo': 'bar'}
foo, blah = pluck(things, 'foo', 'blah')

另外,除了 Joaquin 的 OrderedDict 之外,您还可以对键进行排序并获取值。唯一的问题是您需要按字母顺序指定变量名称并解构字典中的所有内容:

sorted_vals = lambda dict: (t[1] for t in sorted(dict.items()))

things = {'foo': 'bar', 'blah': 'bleh'}
blah, foo = sorted_vals(things)

解决方案 4:

Python 只能“解构”序列,而不能解构字典。因此,要编写所需的内容,您必须将所需的条目映射到适当的序列。就我而言,我能找到的最接近的匹配是(不太性感):

a,b = [d[k] for k in ('a','b')]

这也适用于发电机:

a,b = (d[k] for k in ('a','b'))

以下是完整示例:

>>> d = dict(a=1,b=2,c=3)
>>> d
{'a': 1, 'c': 3, 'b': 2}
>>> a, b = [d[k] for k in ('a','b')]
>>> a
1
>>> b
2
>>> a, b = (d[k] for k in ('a','b'))
>>> a
1
>>> b
2

解决方案 5:

以下是另一种与JS 中的解构赋值类似的方法:

params = {'b': 2, 'a': 1}
a, b, rest = (lambda a, b, **rest: (a, b, rest))(**params)

我们所做的是将 params 字典解包为键值(使用 )(就像在Jochen 的回答中一样),然后我们在 lambda 签名中获取这些值并根据键名分配它们 - 这里有一个奖励 - 我们还得到了一个不存在**于 lambda 签名中的字典,所以如果你有:

params = {'b': 2, 'a': 1, 'c': 3}
a, b, rest = (lambda a, b, **rest: (a, b, rest))(**params)

应用 lambda 之后,剩余变量现在将包含:{'c': 3}

对于从字典中省略不需要的键很有用。

如果你不想保留rest,你可以这样做:

a, b = (lambda a, b, **_): (a, b))(**params)

解决方案 6:

也许你真的想做这样的事?

def some_func(a, b):
  print a,b

params = {'a':1,'b':2}

some_func(**params) # equiv to some_func(a=1, b=2)

解决方案 7:

如果你担心使用本地字典时出现的问题,并且更愿意遵循你原来的策略,那么 Python 2.7 和 3.1 collections.OrderedDicts中的有序字典可以让你按照字典项首次插入的顺序恢复它们

解决方案 8:

使用 Python 3.10,您可以执行以下操作:

d = {"a": 1, "b": 2}

match d:
    case {"a": a, "b": b}:
        print(f"A is {a} and b is {b}")

但它增加了两个额外的缩进级别,并且您仍然必须重复键名。

解决方案 9:

滥用导入系统

from ... import语句允许我们解构和绑定对象的属性名称。当然,它只适用于sys.modules字典中的对象,因此可以使用如下技巧:

import sys, types

mydict = {'a':1,'b':2}

sys.modules["mydict"] = types.SimpleNamespace(**mydict)

from mydict import a, b

一个更严重的黑客攻击是编写一个上下文管理器来加载和卸载模块:

with obj_as_module(mydict, "mydict_module"):
    from mydict_module import a, b

通过将__getattr__模块的方法直接指向__getitem__字典的方法,上下文管理器也可以避免使用SimpleNamespace(**mydict)

请参阅这个答案以了解该想法的实现和一些扩展。

sys.modules你也可以暂时用感兴趣的字典替换整个字典,而import a, b不用from

解决方案 10:

警告 1:如文档中所述,这不能保证适用于所有 Python 实现:

CPython 实现细节:此函数依赖于解释器中的 Python 堆栈框架支持,但并不保证所有 Python 实现都支持该支持。如果在没有 Python 堆栈框架支持的实现中运行,此函数将返回 None。

警告 2:此函数确实使代码更短,但它可能与尽可能明确的 Python 哲学相矛盾。此外,它没有解决 John Christopher Jones 在评论中指出的问题,尽管您可以创建一个类似的函数,该函数使用属性而不是键。这只是一个演示,如果您真的想这样做,您可以这样做!

def destructure(dict_):
    if not isinstance(dict_, dict):
        raise TypeError(f"{dict_} is not a dict")
    # the parent frame will contain the information about
    # the current line
    parent_frame = inspect.currentframe().f_back

    # so we extract that line (by default the code context
    # only contains the current line)
    (line,) = inspect.getframeinfo(parent_frame).code_context

    # "hello, key = destructure(my_dict)"
    # -> ("hello, key ", "=", " destructure(my_dict)")
    lvalues, _equals, _rvalue = line.strip().partition("=")

    # -> ["hello", "key"]
    keys = [s.strip() for s in lvalues.split(",") if s.strip()]

    if missing := [key for key in keys if key not in dict_]:
        raise KeyError(*missing)

    for key in keys:
        yield dict_[key]
In [5]: my_dict = {"hello": "world", "123": "456", "key": "value"}                                                                                                           

In [6]: hello, key = destructure(my_dict)                                                                                                                                    

In [7]: hello                                                                                                                                                                
Out[7]: 'world'

In [8]: key                                                                                                                                                                  
Out[8]: 'value'

此解决方案允许您选择部分键,而不是全部,就像在 JavaScript 中一样。对于用户提供的字典来说,这也是安全的

解决方案 11:

好吧,如果你想要在课堂上实现这些,你可以这样做:

class AttributeDict(dict):
    def __init__(self, *args, **kwargs):
        super(AttributeDict, self).__init__(*args, **kwargs)
        self.__dict__.update(self)

d = AttributeDict(a=1, b=2)

解决方案 12:

寻找其他答案,因为这不会满足字典中意外的顺序。将很快用正确的版本更新它。

试试这个

data = {'a':'Apple', 'b':'Banana','c':'Carrot'}
keys = data.keys()
a,b,c = [data[k] for k in keys]

结果:

a == 'Apple'
b == 'Banana'
c == 'Carrot'

解决方案 13:

我不知道这是否是一种好的风格,但是

locals().update(params)

就可以了。然后你就有了ab以及你的字典中的所有内容,params它们可以作为相应的局部变量。

解决方案 14:

这是一个老话题,但我发现这是一个有用的方法:

data = {'a':'Apple', 'b':'Banana','c':'Carrot'}
for key in data.keys():
    locals()[key] = data[key]

此方法循环遍历字典中的每个键并将变量设置为该名称,然后将关联键的值分配给这个新变量。

测试:

print(a)
print(b)
print(c)

输出

Apple
Banana
Carrot

解决方案 15:

dict在 Python 中,一种简单易行的析构方法:

params = {"a": 1, "b": 2}
a, b = [params[key] for key in ("a", "b")]
print(a, b)
# Output:
# 1 2

解决方案 16:

不,目前在 Python 中没有很好的方法可以做到这一点。

为了在我的书中称得上“好”,解决方案必须避免在作业中重复字典键。

插图:

from operator import itemgetter

params = {'a_very_long_name': 1, 'b': 2}

# Not nice solution, we still have to spell out 'a_very_long_name' twice
a_very_long_name, b = itemgetter('a_very_long_name', 'b')(params)

相反,用 Python 编写此代码的最易读的方法是:

params = {'a_very_long_name': 1, 'b': 2}

a_very_long_name = params['a_very_long_name']
b = params['b']

在 Javascript 中,对象解构有一个特定的语法:

const params = {a_very_long_name: 1, b: 2};

const {a_very_long_name, b} = params;

如果您有 Javascript 背景,您可能会想在 Python 中寻找相同的功能。但 Python 不是 Javascript,它没有此功能。这并不会让 Python 低劣,这只是一个不同的设计决策,处理它的最佳方法是接受它,而不是试图复制编写代码的“Javascript 方式”。

解决方案 17:

最简单、最好的方法是

    client_code, password, login_type = (
        body.get("client_code"),
        body.get("password"),
        body.get("login_type"),
    )

.get() 的优点在于它不会引发错误,这通常发生在 body['password'] 的情况下,默认情况下 .get() 将返回 None

解决方案 18:

我过去在 Python 代码中使用过的最接近的模式是:

def do_the_work(a, b):
  console.log(f'{a}, {b}')


do_the_work(**{'a': 'hello', 'b': 'world'})

解决方案 19:

以下答案将提供真正的解构语法 - 只要您的代码如下所示:

请注意我是如何故意弄乱字典的顺序的——它对此很稳健!

D = {"b":2, "a":1, "c":3}
c, b, a = destructure(D)
print(a, b, c) #Prints "1 2 3"

该函数本身通过分析调用解构函数的行的源代码来工作。

它包含在我的 rp 库中(pip install rp

def destructure(d: dict) -> tuple:
    """
    Extracts values from a dictionary based on the variable names in the
    assignment expression in the calling line of code. 
    Mimics Javascript's destructuring assignment feature.

    The main purpose of this function is to make your code just a little shorter and less redundant.

    It can make your code less redundant, but relies on being able to find
    you source code - an assumption which doesnt always hold (for example,
    in ptpython or the default python repl. Jupyter and rp work fine though.)


    Parameters
    ----------
    d : dict
        The dictionary from which to extract values.

    Returns
    -------
    tuple or value
        A tuple of extracted values, or a single value if only one is extracted.

    Examples
    --------
        d = {'x': 1, 'y': 2, 'z': 3}
        
        # Destructuring into multiple variables
        >>> x, y = destructure(d)
        >>> print(x, y)
        1 2

        # Destructuring into a single variable
        >>> z = destructure(d)
        >>> print(z)
        3

        # Useful for getting kwargs out
        def make_color(**kwargs):
            red,green,blue = destructure(kwargs)

    Pitfalls
    --------
        # Variables on the left-hand side must match keys in the dictionary.
        >>> a, b = destructure(d)
        KeyError: 'Key not found in the provided dictionary.'

        # The function must be used within an assignment operation.
        >>> destructure(d)
        ValueError: 'Destructuring must be used within an assignment operation.'

        # The function doesn't support nested destructuring.
        >>> d = {'p': {'q': 4}}
        >>> p.q = destructure(d)
        AttributeError: 'tuple' object has no attribute 'q'

        # Multi-line assignments are not supported
        >>> a, \n        ... b = destructure(d)
        TypeError: 'Cannot unpack non-iterable int object.'
    """

    import inspect
    import ast

    # Get the source code of the line that called this function
    frame = inspect.currentframe().f_back
    info = inspect.getframeinfo(frame)
    code = info.code_context[0].strip()

    # Use the ast module to parse the source code into a syntax tree
    tree = ast.parse(code)

    try:
        # Find the Assign node (i.e., the assignment operation)
        assign_node = next(node for node in ast.walk(tree) if isinstance(node, ast.Assign))

        # Check if there are multiple assignment targets
        if isinstance(assign_node.targets[0], ast.Tuple):
            # Extract the variable names from the left-hand side of the assignment
            var_names = [target.id for target in assign_node.targets[0].elts]
        else:  # Single target
            var_names = [assign_node.targets[0].id]
    except StopIteration:
        raise Error("Destructuring must be used within an assignment operation.")

    # Use the variable names as keys to get the corresponding values from the dictionary
    values = tuple(d[name] for name in var_names)

    # Return single value instead of a tuple if there is only one value
    if len(values) == 1:
        return values[0]
    
    return values

请注意以下注意事项:

因为它每次调用时都会读取源代码,所以它比手动访问字典要慢!此外,由于它读取源代码,如果在运行时修改源代码,则可能会引起问题。

上述函数的赋值也必须在同一行,因此以下操作不起作用:

D = {"b":2, "a":1, "c":3}
(
    c, 
    b, 
    a,
) = destructure(D)

如果有人有改进此解决方案的建议,请说!

解决方案 20:

由于在 Python >= 3.7 中字典保证保持其插入顺序,这意味着现在这样做是完全安全且惯用的:

params = {'a': 1, 'b': 2}
a, b = params.values()
print(a)
print(b)

输出:

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

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

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

云端的项目管理软件

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

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

内置subversion和git源码管理

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

免费试用