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

Posted

技术标签:

【中文标题】Python - 字典理解中的元组解包【英文标题】:Python - tuple unpacking in dict comprehension 【发布时间】:2012-08-19 03:41:44 【问题描述】:

我正在尝试编写一个函数,将'A=5, b=7' 形式的字符串转换为字典'A': 5, 'b': 7。以下代码 sn-ps 是在 for 主循环中发生的 - 它们将字符串的单个部分转换为单个 dict 元素。

这很好:

s = 'A=5'
name, value = s.split('=')
d = name: int(value)

这不是:

s = 'A=5'
d = name: int(value) for name, value in s.split('=')
ValueError: need more than 1 value to unpack

为什么我不能在字典理解中解包元组?如果我得到这个工作,那么我可以轻松地将整个函数变成一个紧凑的字典理解。

【问题讨论】:

你的问题是“为什么我不能解压元组......”所以我会避免添加到实际上告诉你如何解决这个问题的不断增长的答案,但是一个简单的方法是使用带有可迭代参数的 dict 构造函数。 【参考方案1】:

在您的代码中,s.split('=') 将返回列表:['A', '5']。遍历该列表时,每次都会返回一个字符串(第一次是 'A',第二次是 '5'),因此您无法将该单个字符串解压缩为 2 个变量。

你可以试试:for name,value in [s.split('=')]

更有可能的是,您有一个要拆分的可迭代字符串——然后您的 dict 理解变得简单(2 行):

 splitstrs = (s.split('=') for s in list_of_strings) 
 d = name: int(value) for name,value in splitstrs 

当然,如果你对 1-liners 很着迷,你可以把它组合起来,但我不会。

【讨论】:

如果我让s.split('=') 成为列表的唯一元素,那会起作用吗? for name, value in [s.split('=')]? 不是我写评论的时候 @BigYellowCactus -- 很快。直到今天早上我才注意到自己离得有多近。 (我一直在尝试获得Epic——但我离那个还有一点点距离)。这有点好笑——也许我的答案越来越好,但我有点相信人们才刚刚开始支持我的东西,因为我有将近 15k 的代表...... @BigYellowCactus -- 我知道我的技能因在 SO 上闲逛而有所提高。也许有一天我会用它来学习红宝石或其他东西......我也打算有朝一日获得银色(和金色)gnuplot 徽章,但遗憾的是,该标签上没有足够的问题;)。 @mgilson 我正在为pygame尽我所能,但即使是铜牌也很难获得:-)【参考方案2】:

你当然可以这样做:

>>> s = 'A=5, b=7'
>>> k: int(v) for k, v in (item.split('=') for item in s.split(','))
'A': 5, ' b': 7

但在这种情况下,我只会使用这个更具命令性的代码:

>>> d = 
>>> for item in s.split(','):
        k, v = item.split('=')
        d[k] = int(v)


>>> d
'A': 5, ' b': 7

【讨论】:

我肯定会选择清晰、简单的函数版本,而不是我希望持续时间超过一次运行的代码中的任何内联版本。它肯定更符合 Python 之禅。【参考方案3】:

有些人倾向于相信你会因为使用eval 而下地狱,但是...

s = 'A=5, b=7'
eval('dict(%s)' % s)

或者更好,为了安全起见(感谢 mgilson 指出):

s = 'A=5, b=7'
eval('dict(%s)' % s, '__builtins__': None, 'dict': dict)

【讨论】:

你可以让它更安全一点:eval('dict(%s)'%s,'__builtins__':None,'dict':dict)——我认为使用它可以关闭注入类型攻击的漏洞(尽管我很乐意通过(最好是友好的)评论听到,如果我我错了)。【参考方案4】:

这段代码怎么样:

a="A=5, b=9"
b=dict((x, int(y)) for x, y in re.findall("([a-zA-Z]+)=(\d+)", a))
print b

输出:

'A': 5, 'b': 9

此版本也适用于其他形式的输入,例如

a="A=5 b=9 blabla: yyy=100"

会给你

'A': 5, 'b': 9, 'yyy': 100

【讨论】:

数字仍然是字符串 也许没有必要,但为什么不用\w+ 而不是[a-zA-Z]。唯一真正的区别是您将开始在左侧选择下划线(和数字?)。 当然可以变化,这只是一个例子。此外,_w=5 将匹配并且标识符可能只是字母(来自他的示例)。如果需要,您必须为有效标识符添加更复杂的表达式(_w 对 C/C++ f.e. 不利)。【参考方案5】:
>>> strs='A=5, b=7'

>>> x.split('=')[0].strip():int(x.split('=')[1]) for x in strs.split(",")
'A': 5, 'b': 7

为了可读性,您应该使用普通的 for-in 循环而不是推导式。

strs='A=5, b=7'
dic=
for x in strs.split(','):
  name,val=x.split('=')
  dic[name.strip()]=int(val)

【讨论】:

我考虑过,但我从来不喜欢直接索引列表。我想知道是否有更易读的方法来做到这一点 这也将字符串拆分为必要的两倍,当您唯一“获得”的东西是将代码放入 1 行(难以阅读)而不是 4 行(易于阅读)时,这有点浪费读)行...【参考方案6】:

请参阅 mgilson 对错误发生原因的回答。为了实现你想要的,你可以使用:

d = name: int(value) for name,value in (x.split('=',1) for x in s.split(','))

要考虑空格,请根据需要使用.strip()(例如:x.strip().split('=',1))。

【讨论】:

我正要给出这个作为答案。 :) 如果你想彻底,你可能也想使用.split('=',1),只是为了确保它正确解包。 @mgibsonbr -- 好点。没想到元组解包后会发生什么。尽管该错误可能比其他“没有足够的参数来解包”更有帮助。【参考方案7】:

这个怎么样?

>>> s
'a=5, b=3, c=4'
>>> z.split('=')[0].strip(): int(z.split('=')[1]) for z in s.split(',')
'a': 5, 'c': 4, 'b': 3

【讨论】:

以上是关于Python - 字典理解中的元组解包的主要内容,如果未能解决你的问题,请参考以下文章

Python入门教程第58篇 函数进阶之元组解包

返回语句中的 Python 元组解包

从长度为1的元组解包返回值

Python3 元组解包情况总结

字典 元组

scala 将元组解包到案例类参数和附加的 zip 两个序列中