[英]Append to a dict of lists with a dict comprehension
假設我有一個很大的單詞列表。 例如:
>>> with open('/usr/share/dict/words') as f:
... words=[word for word in f.read().split('\n') if word]
如果我想通過這個單詞列表的第一個字母建立一個索引,這很容易:
d={}
for word in words:
if word[0].lower() in 'aeiou':
d.setdefault(word[0].lower(),[]).append(word)
# You could use defaultdict here too...
結果是這樣的:
{'a':[list of 'a' words], 'e':[list of 'e' words], 'i': etc...}
有沒有辦法用 Python 2.7, 3+ dict 理解來做到這一點? 換句話說,是否可以使用 dict 理解語法在構建 dict 時附加由鍵表示的列表?
IE:
index={k[0].lower():XXX for k in words if k[0].lower() in 'aeiou'}
其中 XXX 在創建index
為鍵執行追加操作或列表創建。
編輯
采納建議和基准:
def f1():
d={}
for word in words:
c=word[0].lower()
if c in 'aeiou':
d.setdefault(c,[]).append(word)
def f2():
d={}
{d.setdefault(word[0].lower(),[]).append(word) for word in words
if word[0].lower() in 'aeiou'}
def f3():
d=defaultdict(list)
{d[word[0].lower()].append(word) for word in words
if word[0].lower() in 'aeiou'}
def f4():
d=functools.reduce(lambda d, w: d.setdefault(w[0], []).append(w[1]) or d,
((w[0].lower(), w) for w in words
if w[0].lower() in 'aeiou'), {})
def f5():
d=defaultdict(list)
for word in words:
c=word[0].lower()
if c in 'aeiou':
d[c].append(word)
產生這個基准:
rate/sec f4 f2 f1 f3 f5
f4 11 -- -21.8% -31.1% -31.2% -41.2%
f2 14 27.8% -- -11.9% -12.1% -24.8%
f1 16 45.1% 13.5% -- -0.2% -14.7%
f3 16 45.4% 13.8% 0.2% -- -14.5%
f5 18 70.0% 33.0% 17.2% 16.9% --
帶有默認 dict 的直接循環最快,其次是 set comprehension 和帶有setdefault
循環。
謝謝你的想法!
No - dict 推導式旨在在每次迭代中生成不重疊的鍵; 他們不支持聚合。 對於此特定用例,循環是有效(在線性時間內)完成任務的正確方法。
字典理解是不可能的(至少很容易或直接)。
這是可能的,但可能會濫用語法,使用集合或列表理解:
# your code:
d={}
for word in words:
if word[0].lower() in 'aeiou':
d.setdefault(word[0].lower(),[]).append(word)
# a side effect set comprehension:
index={}
r={index.setdefault(word[0].lower(),[]).append(word) for word in words
if word[0].lower() in 'aeiou'}
print r
print [(k, len(d[k])) for k in sorted(d.keys())]
print [(k, len(index[k])) for k in sorted(index.keys())]
印刷:
set([None])
[('a', 17094), ('e', 8734), ('i', 8797), ('o', 7847), ('u', 16385)]
[('a', 17094), ('e', 8734), ('i', 8797), ('o', 7847), ('u', 16385)]
集合setdefault()
在迭代words
列表后生成一個帶有setdefault()
方法結果的集合。 在這種情況下set([None])
的總和。 它還會產生您想要的生成列表字典的副作用。
它不像直循環結構那樣可讀(恕我直言),應該避免(恕我直言)。 它不會更短,也可能不會顯着更快。 這是關於 Python 的有趣瑣事而不是有用——恕我直言......也許是為了贏得賭注?
我會使用filter
:
>>> words = ['abcd', 'abdef', 'eft', 'egg', 'uck', 'ice']
>>> index = {k.lower() : list(filter(lambda x:x[0].lower() == k.lower(),words)) for k in 'aeiou'}
>>> index
{'a': ['abcd', 'abdef'], 'i': ['ice'], 'e': ['eft', 'egg'], 'u': ['uck'], 'o': []}
這不完全是對字典的理解,而是:
reduce(lambda d, w: d.setdefault(w[0], []).append(w[1]) or d,
((w[0].lower(), w) for w in words
if w[0].lower() in 'aeiou'), {})
不回答字典理解的問題,但它可能有助於搜索這個問題的人。 在簡化的示例中,當將不斷增長的列表填充到新字典中時,請考慮在列表推導式中調用一個函數,誠然,沒有什么比循環更好的了。
def fill_lists_per_dict_keys(k, v):
d[k] = (
v
if k not in d
else d[k] + v
)
# global d
d = {}
out = [fill_lists_per_dict_keys(i[0], [i[1]]) for i in d2.items()]
out
只是為了抑制每個循環的None
輸出。
如果您想在運行時甚至在列表理解中使用新字典,或者如果您遇到字典被每個循環覆蓋的另一個原因,請檢查以在腳本開頭使用global d
使其全局化(注釋掉是因為這里不需要)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.