Python - 字典推导式中的元组解包

14
我想编写一个函数,将形如'A=5, b=7'的字符串转换为字典{'A': 5, 'b': 7}。以下代码片段是主要for循环中发生的事情 - 它们将字符串的单个部分转换为单个字典元素。
这是可以的:
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

为什么我不能在字典推导式中解包元组?如果我能解决这个问题,那么我就可以将整个函数变成一个紧凑的字典推导式。

8个回答

22

在你的代码中,s.split('=') 将返回列表:['A', '5']。当迭代该列表时,每次只返回一个字符串(第一次是 'A',第二次是 '5'),因此你无法将该单个字符串解包成两个变量。

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

更有可能的情况是,你拥有一个字符串可迭代对象,你想要对其进行拆分 - 然后你的字典推导变得简单(只需两行):

 splitstrs = (s.split('=') for s in list_of_strings) 
 d = {name: int(value) for name,value in splitstrs }
当然,如果你追求一行代码的话,你可以将它结合起来,但我不会这么做。

9

当然,你可以这样做:

>>> 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}

6

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

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

或者更好的方法是为了安全起见(感谢mgilson指出):
s = 'A=5, b=7'
eval('dict(%s)' % s, {'__builtins__': None, 'dict': dict})

1
你可以让它更安全一些:eval('dict(%s)'%s,{'__builtins__':None,'dict':dict}) -- 我认为使用这种方法可以防止注入攻击的漏洞(不过如果我有误,我很乐意通过友好的评论听取反馈)。 - mgilson

3

请参考mgilson的回答以了解错误原因。为了实现您想要的功能,您可以使用以下方法:

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

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

2
这段代码怎么样:
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}

2
>>> 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

自 Python 3.8 开始, 您可以使用海象运算符 (:=) 来执行此类操作。它允许在表达式中间分配变量(在本例中,将由 .split('=') 创建的列表分配给 kv)。

s = 'A=5, b=7'
{(kv := item.split('='))[0]: int(kv[1]) for item in s.split(', ')}
# {'A': 5, 'b': 7}

一个特点是它会在定义的范围之外泄漏已赋值的变量kv。如果你想避免这种情况,可以采用嵌套for循环的方式,其中内部循环为单例列表(正如mgilson的答案所建议的那样)。
{k: int(v) for item in s.split(', ') for k,v in [item.split('=')]}

自从Python 3.9版本, 对于单元素列表的循环进行了优化,使其速度与简单赋值一样快,即y in [expr]y = expr的速度相同。


0

这个怎么样?

>>> 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}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接