【问题标题】:Is an intermediate list necessary in a multi-level list comprehension在多级列表理解中是否需要中间列表
【发布时间】:2018-01-18 22:01:18
【问题描述】:

这是一个具体的例子:

my_dict={k:int(encoded_value) 
         for (k,encoded_value) in 
             [encoded_key_value.split('=') for encoded_key_value in 
              many_encoded_key_values.split(',')]}

问题是关于内部列表[],可以避免吗,例如:

# This will not parse
my_dict={k:int(encoded_value) 
         for (k,encoded_value) in 
             encoded_key_value.split('=') for encoded_key_value in 
             many_encoded_key_values.split(',')}

...,这是无效的语法:

NameError: name 'encoded_key_value' is not defined

样本数据:aa=1,bb=2,cc=3,dd=4,ee=-5

【问题讨论】:

  • 对于这个特定的示例,甚至来自astliteral_eval 可能对某些文本操作有所帮助。
  • @Ev.Kounis,我也试过result = ast.literal_eval('dict('+many_encoded_key_values+')')。但我很好奇,它不起作用:... raise ValueError('malformed node or string: ' + repr(node))
  • @RomanPerekhrest 我试过res = ast.literal_eval('{"' + many_encoded_key_values.replace('=', '":').replace(',', ',"') + '}'),但它看起来太丑了,无法发布。
  • 哇,一些严重的案例特定的解析黑客,有:)
  • @MichaelGoldshteyn 脖子上的疼痛引用了abcs。但是@Roman 有一个非常有效的观点。为什么他的literal_eval 不起作用?。

标签: python python-3.x python-3.6


【解决方案1】:

如前所述,生成器表达式将增强您的方法,避免创建内部列表。但是有一种更短的方法来获得所需的结果,使用re.findall() 函数:

result = {k:int(v) for k,v in re.findall(r'(\w+)=([^,]+)', many_encoded_key_values)}
print(result)

输出:

{'dd': 4, 'aa': 1, 'bb': 2, 'ee': -5, 'cc': 3}

替代方法是使用返回'callable_iterator' 实例的re.finditer() 函数:

result = {m.group(1):int(m.group(2)) for m in re.finditer(r'(\w+)=([^,]+)', many_encoded_key_values)}

【讨论】:

  • 一种有趣的方法,使用正则表达式至少在逻辑上将表达式“强度降低”到一个级别。
  • @MichaelGoldshteyn,是的。如果我们有与号分隔的项目,那就更容易了many_encoded_key_values = 'aa=1,7&bb=2&cc=3&dd=4&ee=-5'
  • findall() 确实建立了list
  • @Ev.Kounis,确实如此,但它也避免了所有拆分操作(此外,我的方法想法是更短的方法
  • @MichaelGoldshteyn finditer 是一个生成器。
【解决方案2】:

您可以通过使用中间生成器表达式来避免创建中间列表:

my_dict={k:int(encoded_value)
         for (k,encoded_value) in
             (encoded_key_value.split('=') for encoded_key_value in
              many_encoded_key_values.split(','))}

语法上几乎是一样的;不是先生成中间列表然后使用元素,而是动态消耗元素。


让这个过于冗长,您可以使用由生成器组成的“数据管道”:

eq_statements = (item.strip() for item in many_encoded_key_values.split(','))
var_i = (var_i.split('=') for var_i in eq_statements)
my_dict = {var: int(i) for var, i in var_i}
print(my_dict)

(不幸的是,.split 没有返回生成器,因此考虑到节省空间,这并没有多大用处……对于处理大文件,这样的事情可能会派上用场。)

找到this answer,其中split 作为迭代器。以防万一……

【讨论】:

  • 有趣,这避免了临时存储并迭代项目。
  • 是的,添加了一个过于冗长的变体,应该说明它的(惰性)生成器评估......
  • 谢谢,这看起来是 n 级表达式使代码更具可读性的好方法。
【解决方案3】:

FWIW,这是一种功能性方法:

def convert(s):
    k, v = s.split('=')
    return k, int(v)

d = dict(map(convert, data.split(',')))
print(d)

输出

{'aa': '1', 'bb': '2', 'cc': '3', 'dd': '4', 'ee': '-5'}

【讨论】:

  • 不过,我想要整数作为值。请更新您的答案。
  • @MichaelGoldshteyn 啊,好的。
  • @MichaelGoldshteyn 在那种情况下,hiro 主角的第一种方法可能是最好的方法。但是,我添加了一个避免中间列表的功能版本。在 Python 2 中,map 确实构建了一个列表,但在 Python 3 中没有。
【解决方案4】:

一个简单而紧凑的变体,非常接近您最初的尝试:

d = {v.strip(): int(i) for s in data.split(',') for v, i in (s.split('='),)}

唯一的额外“技巧”是将s.split('=') 包装在一个元组中(用括号括起来:(s.split('='),)),以便在同一个for 迭代中获得split 的两个元素。其余的都很简单。

【讨论】:

  • ...对不起,其他答案。但我觉得这应该以我首先介绍的更简单的方式实现。这对我来说感觉更自然。
猜你喜欢
  • 2019-07-17
  • 1970-01-01
  • 2018-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-28
  • 1970-01-01
相关资源
最近更新 更多