为啥字典可以解包为元组?

Posted

技术标签:

【中文标题】为啥字典可以解包为元组?【英文标题】:Why can a dictionary be unpacked as a tuple?为什么字典可以解包为元组? 【发布时间】:2014-06-09 17:19:04 【问题描述】:

今天,我看到一个没有抛出异常的语句。谁能解释一下它背后的理论?

>>> x, y = 'a': 2, 'b': 5
>>> x
'a'
>>> y
'b'

【问题讨论】:

由于下面没有提到,我会说这个设计决策很可能受到实用程序的影响。当一个 dict 给你它的键时,它隐含地让你访问它的值。如果它返回它的值,那么访问它的键将更加困难。 【参考方案1】:

迭代 dict 会遍历键。由于您的 dict 文字正好有两个键,因此您可以将其解压缩为 2 元组。

这可能不是一个好的做法,因为(在 python 3.7 之前,或者可能在其他一些实现的更早版本)dicts 是无序的,x == 'b'y == 'a' 将是该代码的完全合法的结果。

【讨论】:

感谢您提及它们是无序的。 从 Python 3.6 开始,字典是有序的。 @geoffspear:这是 python 3.7+ 中的语言特性【参考方案2】:

背后没有火箭科学。 dict 是一个可迭代对象,它在每次迭代中返回键。 tuple() 可以接收任何可迭代的参数(只要它们是有限的),所以:

>>>tuple('a': 2, 'b': 5)
('a','b')

看到这一点,很容易推断解包将如图所示。此外,任何 finite 可迭代对象都可以解包:

>>> i = iter(range(3))
>>> a,b,c = i
>>> a,b,c
(0, 1, 2)

【讨论】:

同样我们可以使用>>> list('a': 2, 'b': 5) :) 没有火箭科学:D!+1【参考方案3】:

在Python中,每个iterable都可以解包1

>>> x,y,z = [1, 2, 3]  # A list
>>> x,y,z
(1, 2, 3)
>>> x,y,z = 1, 2, 3  # A tuple
>>> x,y,z
(1, 2, 3)
>>> x,y,z = 1:'a', 2:'b', 3:'c'  # A dictionary
>>> x,y,z
(1, 2, 3)
>>> x,y,z = (a for a in (1, 2, 3))  # A generator
>>> x,y,z
(1, 2, 3)
>>>

此外,因为遍历字典只返回它的键:

>>> for i in 1:'a', 2:'b', 3:'c':
...     print i
...
1
2
3
>>>

解包字典(迭代它)同样只解包它的键。


1其实我应该说每个iterable都可以解包只要要解包的名字等于iterable的长度:

>>> a,b,c = [1, 2, 3]  # Number of names == len(iterable)
>>>
>>> a,b = [1, 2, 3]  # Too few names
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 2)
>>>
>>> a,b,c,d = [1, 2, 3]  # Too many names
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: need more than 3 values to unpack
>>>

但这仅适用于 Python 2.x。在 Python 3.x 中,您有 extended iterable unpacking,它允许您将任何(有限)大小的迭代解压缩为您需要的名称:

>>> # Python 3.x interpreter
...
>>> a, *b, c = [1, 2, 3, 4]
>>> a, b, c
(1, [2, 3], 4)
>>>
>>> a, *b = [1, 2, 3, 4]
>>> a, b
(1, [2, 3, 4])
>>>
>>> *a, b, c = [1, 2, 3, 4]
>>> a, b, c
([1, 2], 3, 4)
>>>

【讨论】:

我猜,这意味着每个迭代都可以解包? 是的,但是对于可变的可迭代对象(长度和内容)通常没有意义,对于无序的可迭代对象更是如此。您将无法确定密钥在哪里结束,也无法确定您提供了足够的变量来解包。 @aIKid,不是无限的 如果dict中只有一个键,是不是很混乱? x='a':3, print x 给出 'a': 3 而如果我们执行 x,='a':3 然后打印 x 则结果为 'a'。 @nik_kgp - 第一段代码是将字典本身分配给x。然而,第二部分是将字典解包x【参考方案4】:

在可迭代上下文中,dicts 被视为键的(无序)集合,这就是您在执行 list(some_dict) 时得到的,这与在 dict 上调用 keys() 相同:

>>> d = 'a': 3, 'b': 5
>>> list(d)
['a', 'b']
>>> d.keys()
['a', 'b']

不过,您还可以做得更多。

如果您先将 dict 的两个键 值解压缩为一对列表,则可以解包:

>>> d = 'a': 3, 'b': 5
>>> d_pairs = d.items()
>>> print d_pairs
[('a', 3), ('b', 5)]
>>> ((k1, v1), (k2, v2)) = d_pairs
>>> print k1, v1, k2, v2
a 3 b 5

或者如果你只是想要成对

>>> p1, p2 = d_pairs
>>> print p1, p2
('a', 3) ('b', 5)

或者说,只是键:

>>> ((k1, _), (k2, _)) = d_pairs
>>> print k1, k2
a b

等等

当然,因为字典——我的意思是一般来说,不仅仅是在 Python 中——以无序的方式包含它们的项目,items()(在 Python 中)也会以看似任意的顺序返回它们,因此有是无法知道哪个键会存储在哪个变量中:

>>> ((k1, v1), (k2, v2)) = 'bar': 3, 'foo': 5.items()
>>> print k1, v1, k2, v2
foo 5 bar 3

如您所见,items() 返回的对的顺序与其定义顺序相反。

【讨论】:

+1 用于提及顺序,在拆包时不一定保持原样。【参考方案5】:

当你遍历一个字典时,你会得到它的键

data = 'a': 2, 'b': 5
for key in data:
    print key

解包只不过是遍历对象并将元素放入给定的变量中:

keys = tuple(data) # gives ('a', 'b')
x, y = ('a', 'b')

【讨论】:

以上是关于为啥字典可以解包为元组?的主要内容,如果未能解决你的问题,请参考以下文章

Python - 字典理解中的元组解包

为啥解包元组会导致语法错误?

python 第34课 字典 _ 序列解包用于列表元组字典

python 第34课 字典 _ 序列解包用于列表元组字典

python中的解包

python ValueError:在元组中解包的值太多