解构 - 绑定字典内容

hat*_*rix 45 python sorting dictionary

I am trying to 'destructure' a dictionary and associate values with variables names after its keys. Something like

params = {'a':1,'b':2}
a,b = params.values()
Run Code Online (Sandbox Code Playgroud)

But since dictionaries are not ordered, there is no guarantee that params.values() will return values in the order of (a, b). Is there a nice way to do this?

小智 41

from operator import itemgetter

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

a, b = itemgetter('a', 'b')(params)
Run Code Online (Sandbox Code Playgroud)

除了精心设计的lambda函数或字典理解之外,还可以使用内置库.

  • 这可能是公认的答案,因为这是最Python化的方法。您甚至可以从同一标准库模块(适用于对象属性(obj.a))中扩展使用attrgetter的答案。与JavaScript的主要区别是JavaScript,其中的obj.a === obj [“ a”]。 (4认同)
  • @TasawarHussain 感谢上帝,它抛出了“KeyError”。它还应该做什么? (4认同)
  • 如果字典中不存在该键,则会引发“KeyError”异常 (3认同)
  • 但是您现在在解构语句中输入 a 和 b 两次 (2认同)
  • @JohnChristopherJones 这对我来说似乎不太自然,使用现有的东西并不意味着它可以理解。我怀疑很多人会立即理解真正的代码。另一方面,正如建议的那样,“a, b = [d[k] for k in ('a','b')]”更加自然/可读(形式更常见)。这仍然是一个有趣的答案,但这不是最直接的解决方案。 (2认同)

Sha*_*umo 26

比Jochen的建议更少重复的一种方法是使用辅助函数.这样可以灵活地按任意顺序列出变量名称,并且只解构dict中的变量的子集:

pluck = lambda dict, *args: (dict[arg] for arg in args)

things = {'blah': 'bleh', 'foo': 'bar'}
foo, blah = pluck(things, 'foo', 'blah')
Run Code Online (Sandbox Code Playgroud)

此外,您可以对键进行排序并获取值,而不是joaquin的OrderedDict.唯一的捕获是您需要按字母顺序指定变量名称并解构dict中的所有内容:

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

things = {'foo': 'bar', 'blah': 'bleh'}
blah, foo = sorted_vals(things)
Run Code Online (Sandbox Code Playgroud)

  • 这是一个小问题,但是如果你要将一个`lambda`分配给一个变量,你也可以使用普通的函数语法和`def`. (3认同)
  • 您已经从标准库中的“operator”实现了“itemgetter”。:) (3认同)

Syl*_*oux 14

Python只能"解构"序列,而不是字典.因此,要编写您想要的内容,您必须将所需的条目映射到正确的序列.就我自己而言,我能找到的最接近的匹配是(不是很性感):

a,b = [d[k] for k in ('a','b')]
Run Code Online (Sandbox Code Playgroud)

这也适用于发电机:

a,b = (d[k] for k in ('a','b'))
Run Code Online (Sandbox Code Playgroud)

这是一个完整的例子:

>>> 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
Run Code Online (Sandbox Code Playgroud)


dec*_*ory 11

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

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

match d:
    case {"a": a, "b": b}:
        print(f"A is {a} and b is {b}")
Run Code Online (Sandbox Code Playgroud)

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


Joc*_*zel 8

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

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

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

some_func(**params) # equiv to some_func(a=1, b=2)
Run Code Online (Sandbox Code Playgroud)


Eri*_*rik 8

(Ab)使用导入系统

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

import sys, types

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

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

from mydict import a, b
Run Code Online (Sandbox Code Playgroud)

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

with obj_as_module(mydict, "mydict_module"):
    from mydict_module import a, b
Run Code Online (Sandbox Code Playgroud)

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

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

sys.modules人们还可以暂时用感兴趣的字典替换整个字典,并且import a, b不用from.


joa*_*uin 7

如果您害怕使用locals字典所涉及的问题,并且您更喜欢遵循原始策略,则可以使用python 2.7和3.1 collections.OrderedDicts中的 Ordered Dictionaries按照第一次插入的顺序恢复字典项

  • 从 3.6+ 开始就有保证。 (7认同)
  • 当前,在3.5+中,所有字典都是有序的。现在还没有“保证”,这意味着它可能会改变。 (2认同)

dec*_*ory 7

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

\n\n
\n

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

\n
\n\n

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

\n\n
def destructure(dict_):\n    if not isinstance(dict_, dict):\n        raise TypeError(f"{dict_} is not a dict")\n    # the parent frame will contain the information about\n    # the current line\n    parent_frame = inspect.currentframe().f_back\n\n    # so we extract that line (by default the code context\n    # only contains the current line)\n    (line,) = inspect.getframeinfo(parent_frame).code_context\n\n    # "hello, key = destructure(my_dict)"\n    # -> ("hello, key ", "=", " destructure(my_dict)")\n    lvalues, _equals, _rvalue = line.strip().partition("=")\n\n    # -> ["hello", "key"]\n    keys = [s.strip() for s in lvalues.split(",") if s.strip()]\n\n    if missing := [key for key in keys if key not in dict_]:\n        raise KeyError(*missing)\n\n    for key in keys:\n        yield dict_[key]\n
Run Code Online (Sandbox Code Playgroud)\n\n
In [5]: my_dict = {"hello": "world", "123": "456", "key": "value"}                                                                                                           \n\nIn [6]: hello, key = destructure(my_dict)                                                                                                                                    \n\nIn [7]: hello                                                                                                                                                                \nOut[7]: \'world\'\n\nIn [8]: key                                                                                                                                                                  \nOut[8]: \'value\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n


Dan*_*non 7

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

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

a, b = params['a'], params['b']
Run Code Online (Sandbox Code Playgroud)

  • 哈哈,是的,`b, a = params["b"], params["a"]` (6认同)

小智 6

这是另一种方法,类似于JS中的解构分配如何工作:

params = {'b': 2, 'a': 1}
a, b, rest = (lambda a, b, **rest: (a, b, rest))(**params)
Run Code Online (Sandbox Code Playgroud)

我们所做的是将params字典解压缩为键值(使用**)(如Jochen的回答所示),然后我们将这些值取入了lambda签名并根据键名进行了分配-这是一个奖励-我们还可以获得什么是一本字典不能在lambda的签名,所以如果你有:

params = {'b': 2, 'a': 1, 'c': 3}
a, b, rest = (lambda a, b, **rest: (a, b, rest))(**params)
Run Code Online (Sandbox Code Playgroud)

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

对于从字典中删除不需要的键很有用。

希望这可以帮助。