【问题标题】:For loop to dictionary comprehension correct translation (Python)For循环到字典理解正确翻译(Python)
【发布时间】:2021-02-20 13:28:59
【问题描述】:

我需要在列表中为字母表中的每个字母找到最长的字符串。 我的第一个直截了当的方法如下所示:

alphabet = ["a","b", ..., "z"]
text = ["ane4", "anrhgjt8", "andhjtje9", "ajhe5", "]more_crazy_words"]
result = {key:"" for key in alphabet} # create a dictionary

# go through all words, if that word is longer than the current longest, save it
for word in text: 
    if word[0].lower() in alphabet and len(result[word[0].lower()]) < len(word):
        result[word[0].lower()] = word.lower()
print(result)

返回:

{'a': 'andhjtje9'}

正如它应该做的那样。

为了练习字典理解,我试图用一行来解决这个问题:

result2 = {key:"" for key in alphabet}
result2 = {word[0].lower(): word.lower() for word in text if word[0].lower() in alphabet and len(result2[word[0].lower()]) < len(word)}

我刚刚将 if 语句复制到了理解循环中... 然而结果 2 是:

{'a': 'ajhe5'}

有人能解释一下为什么会这样吗?我觉得我做的和第一个循环完全一样...... 感谢您的帮助!

【问题讨论】:

  • 您使用result2 的先前值,在计算字典理解之前一直将空字符串作为值
  • for 需要在alphabet 上并通过过滤第一个字符并构造(length, string) 的元组来找到最大长度的单词,然后使用键函数找到最大值,然后提取字符串找到的最大值
  • 您可以申请groupby(){g: i for g, (*_, i) in groupby(sorted(text, key=lambda x: (len(x), x[0])), itemgetter(0)) if 96 &lt; ord(g) &lt; 123}。导入:groupby()itemgetter()(您可以将 itemgetter(0) 替换为 lambda x: x[0]。我用ascii字符码过滤a .. z,检查ASCII table
  • @PatrickArtner,对不起我的错误......它返回'andhjtje9'......我将编辑帖子
  • @rioV8 谢谢,这基本上就是我正在寻找的解释!我没有意识到它仍然将值与空字符串进行比较,因为新的 dict 尚未创建,而在“经典” for 循环中它是!

标签: python for-loop dictionary-comprehension


【解决方案1】:

List / Dict / Set - 理解不能在构建自身时引用它们自己 - 这就是为什么你没有得到你想要的。

可以使用复杂的字典理解来执行此操作 - 在排序列表上的 collections.groupby 的帮助下,这可能如下所示:

from string import ascii_lowercase
from itertools import groupby 

text = ["ane4", "anrhgjt8", "andhjtje9", "ajhe5", "]more_crazy_words"] 

d = {key:sorted(value, key=len)[-1] 
 for key,value in groupby((s for s in sorted(text) 
                           if s[0].lower() in frozenset(ascii_lowercase)), 
                          lambda x:x[0].lower())}
print(d) # {'a': 'andhjtje9'}

text = ["ane4", "anrhgjt8", "andhjtje9", "ajhe5", "]more_crazy_words"] 
 
d = {key:next(value) for key,value in groupby(
    (s for s in sorted(text, key=lambda x: (x[0],-len(x))) 
     if s[0].lower() in frozenset(ascii_lowercase)), 
     lambda x:x[0].lower())}

print(d) # {'a': 'andhjtje9'}

或其他几种方式......但你为什么要这样做?

将它用作 for 循环更简洁,更易于理解,在这种情况下,可能更好地遵循 python 的禅宗。

通过运行了解python的禅:

import this

【讨论】:

  • 我也更喜欢基本循环版本...我只是在尝试练习它,但很困惑为什么这不起作用。不过谢谢你的回答!很好的解决问题:)
  • 如果您只是按首字母和长度预先排序(即最后进入,获胜),则不需要 Groupby:{i[0]: i for i in sorted(map(str.lower, text), key=lambda i: (i[0], len(i))) if i[0] in alphabet}
  • @ekh with groupby I 避免一遍又一遍地重新设置一个键的值 - 您将每个键的值设置为所有值,从最短的开始,然后用更长的值覆盖它们,直到最长的值存在。有效的方法,我更喜欢分组结构。谢谢你的建议。
  • @ekhumoro,说“不需要”有点强硬,因为您提供的方法完全不同,它依赖于每次都会重写键值的事实。谈到“优化”,您可以通过降低 lambda 表达式中的第一个字符来避免使用 map()
  • @PatrickArtner 重新设置值正是惯用的 for 循环解决方案所做的(即最后进入,获胜)。 OP 尝试转换为 dict 理解失败,因为它需要对其自身进行查找。我的解决方案成功了,因为它通过对输入进行预排序来避免这种查找。这表明,在不使用groupby 的情况下,可以非常接近 OP 试图实现的目标。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-08
  • 2021-11-22
相关资源
最近更新 更多