解构-绑定字典内容

我正在尝试“解构”一个字典,并将值与其键后面的变量名相关联

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

但是由于字典是不排序的,所以不能保证 params.values()将返回 (a, b)顺序的值。有什么好办法吗?

84853 次浏览

我不知道这是不是好风格,但是

locals().update(params)

会有用的。然后,你有 ab和任何在你的 params字典可作为相应的局部变量。

如果你想在课堂上学到这些,你可以这样做:

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


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

如果您担心使用局部词典会出现问题,并且更愿意遵循最初的策略,那么 python 2.7和3.1 集合,有序词典中的 OrderedDictionaries 允许您按照首次插入字典项的顺序恢复它们

也许你真的想这么做?

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


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


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

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

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


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

而且,您可以对键进行排序并获得值,而不是使用 joaquin 的 OrderedDect。唯一的问题就是你需要在字母顺序中指定你的变量名,然后解构 dict 中的所有内容:

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


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

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

寻找其他答案,因为这不会迎合字典中意想不到的顺序。 将在不久的将来用一个正确的版本更新它。

试试这个

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'
from operator import itemgetter


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


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

与其使用复杂的 lambda 函数或字典理解,不如使用内置的库。

这里有另外一种方法可以类似于 破坏性转让在 JS 中的工作方式:

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

我们所做的就是将 params 字典解压缩为键值(使用 * *)(就像在 约亨的回答中一样) ,然后我们在 lambda 签名中获取这些值,并根据键名赋值它们——这里有一个好处——我们还获得了 lambda 签名中 没有的字典,所以如果你有:

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

在应用了 lambda 之后,rest 变量现在将包含: {‘ c’: 3}

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

希望这个能帮上忙。

基于@ShawnFumo 的回答,我想到了这个:

def destruct(dict): return (t[1] for t in sorted(dict.items()))


d = {'b': 'Banana', 'c': 'Carrot', 'a': 'Apple' }
a, b, c = destruct(d)

(请注意 dict 中项目的顺序)

警告1: 如文档中所述,这并不能保证在所有 Python 实现上都能正常工作:

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

警告2: 此函数确实使代码更短,但它可能与尽可能显式的 Python 哲学相矛盾。此外,它没有解决克里斯托弗·琼斯在评论中指出的问题,尽管你可以做一个类似的函数,使用属性而不是键。这只是一个示范,你 可以做,如果你真的想!

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 中那样。它对于用户提供的字典也是安全的

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

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

产出:

1
2

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

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


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

使用 Python 3.10,您可以:

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


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

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

(Ab)使用进口系统

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__方法直接指向 dict 的 __getitem__方法,上下文管理器还可以避免使用 SimpleNamespace(**mydict)

请参阅 这个答案以获得实现和该思想的一些扩展。

一个人也可以暂时取代整个 sys.modules的判决与感兴趣的判决,并做 import a, b没有 from

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

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