在一行代码中使用Python的strip和split两次

3
我有一个像这样的字符串。
PARAMS = 'TEST = xy; TEST2= klklk '

我想将其分成两部分,一次在“;”处分割,第二次在“=”处分割,然后将其放入字典中。
可以使用以下代码行完成:
dict(item.split("=") for item in PARAMS.split(";"))

and get:

{' TEST2': ' klklk ', 'TEST ': ' xy'}

我现在也想在将它们放入字典之前删除键和值。有没有一种优雅的方式可以在 Python 中用一行代码实现?

lambda和for循环。 - Torxed
5个回答

5

我不确定你所说的“优雅”是什么,但这个方案可行:

dict((i.strip() for i in item.split("=")) for item in PARAMS.split(";"))

3
dict([i.strip() for i in item.split("=")] for item in PARAMS.split(";"))

这比@aIKid的解决方案运行速度快得多 :)
PARAMS = 'TEST = xy; TEST2= klklk '
from timeit import timeit
print timeit('dict((i.strip() for i in item.split("=")) for item in PARAMS.split(";"))', "from __main__ import PARAMS")
print timeit('dict([i.strip() for i in item.split("=")] for item in PARAMS.split(";"))', "from __main__ import PARAMS")

输出

18.7284784281
9.16360774723

2
@aIKid:就像在 列表推导和 str.join() 中一样,dict() 必须为每个 (key, value) 对都有序列以便验证它们的长度。再加上在 Python 2.7 中 列表推导不使用新作用域,但生成器表达式会使用(使得列表推导的固定成本更低),你就会明白为什么在这里使用列表推导来处理 (key, value) 对是更快的。 - Martijn Pieters
@MartijnPieters 哇哦,感谢您的解释!我会记住的。再次感谢! - aIKid
@Martijn Pieters:但在这种情况下,根本不进行内联比列表推导还要快(因为第二个循环根本不应该是循环,但列表推导缺乏必要的语法能力)。 - kriss
@kris:确实;避免循环仍然更快。有时候,一行代码并不值得这么做! - Martijn Pieters
@kriss 我们如何在不使用第二个循环的情况下完成这个任务? - thefourtheye
@thefourtheye:第二个 split 应该只返回 key 和 value,其他配置都是错误的。我们想要剥去两者,但它仍然只有两个值。但在列表推导的上下文中,我们没有太多选择,只能欺骗它成为两个循环(或执行两次相同值的分割,可能更糟)。 - kriss

2
也许可以这样表述:

dict(map(lambda x: x.strip(), item.split("=")) for item in PARAMS.split(";"))

或者另一种更加优雅的版本:

dict((l[i].strip(), l[i+1].strip()) for i in range(2) for l in [re.split(';|=', PARAMS)])

当然,如果您将其视为“模糊”的同义词,那么这只是一种优雅的方式。但是当我们寻找一行代码时,这不是我们想要的吗?
为了解决这个问题,我可能会写下以下代码:
d = dict(); 
for item in PARAMS.split(";"):
    key, value = item.split("=")
    d[key.strip()] = value.strip()

这个解决方案比目前提出的所有答案都更易读且更快,而且我甚至没有进行任何优化,因此可能不是最佳解决方案。

不要只听言语,可以测试不同的解决方案来检查:

PARAMS = 'TEST = xy; TEST2= klklk '

from timeit import timeit

print 'obfuscated', timeit('dict((l[i].strip(), l[i+1].strip()) for i in range(2) for l in [re.split(";|=", PARAMS)])', "from __main__ import PARAMS; import re")
print 'tuple', timeit('dict((i.strip() for i in item.split("=")) for item in PARAMS.split(";"))', "from __main__ import PARAMS")
print 'regex', timeit('dict(re.findall(r"(\S+)\s*=\s*([^\s;]+)", PARAMS))', "from __main__ import PARAMS; import re")
print 'lambda', timeit('dict(map(lambda x: x.strip(), item.split("=")) for item in PARAMS.split(";"))', "from __main__ import PARAMS; import re")
print 'list comprehension', timeit('dict([i.strip() for i in item.split("=")] for item in PARAMS.split(";"))', "from __main__ import PARAMS")
print 'replace spaces', timeit('dict(item.split("=") for item in PARAMS.replace(" ", "").split(";"))', "from __main__ import PARAMS; import re")

print 'not one line', timeit(
'''
    d = dict(); 
    for item in PARAMS.split(";"):
        key, value = item.split("=")
        d[key.strip()] = value.strip()
    d
''',
"from __main__ import PARAMS")

以下是时间结果:
- 模糊化:7.36826086044 - 元组:4.49374079704 - 正则表达式:3.61684799194 - Lambda:3.51627087593 - 列表推导式:2.90777206421 - 替换空格:2.46001887321 - 非单行代码:1.71015286446
它本身说明了问题。
注:“非单行代码”更快的原因可能是因为它避免了创建不必要的列表结构,直接将值存储在字典中。但这是显而易见的,甚至是无意识的。

1
或者,另一种选择是:
import re
text = 'TEST = xy; TEST2= klklk '
params = dict(re.findall(r'(\S+)\s*=\s*([^\s;]+)', text))
# {'TEST': 'xy', 'TEST2': 'klklk'}

1
如果你的键或值中没有空格,那么你可以使用一个 replace 方法轻松地消除所有空格:
>>> dict(item.split("=") for item in PARAMS.replace(" ", "").split(";"))
{'TEST': 'xy', 'TEST2': 'klklk'}

这将比strip更彻底地消除空格:
>>> PARAMS = 'TEST 3 = there should be spaces between these words '
>>> dict(item.split("=") for item in PARAMS.replace(" ", "").split(";"))
{'TEST3': 'thereshouldbespacesbetweenthesewords'}

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