14

大量の単語リストがあるとします。例:

>>> 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+の辞書理解でこれを行う方法はありますか? 言い換えれば、辞書が構築されているときにキーによって表されるリストを追加する辞書内包構文を使用することは可能ですか?

すなわち:

  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 と loop with が続きますsetdefault

アイデアをありがとう!

4

5 に答える 5

15

いいえ - dict 内包表記は、反復ごとに重複しないキーを生成するように設計されています。集計はサポートされていません。この特定の使用例では、タスクを効率的に (線形時間で) 達成するための適切な方法はループです。

于 2012-06-30T18:18:52.127 に答える
7

辞書の理解では(少なくとも簡単にまたは直接的に)不可能です。

セット内包表記またはリスト内包表記を使用することは可能ですが、構文を悪用する可能性があります。

# 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ます。この場合の合計set([None])。また、リストの辞書を作成するという望ましい副作用も生み出します。

これは、ストレート ループ コンストラクトほど読みにくい (IMHO) ため、避ける必要があります (IMHO)。それは短くはなく、おそらく実質的に速くもありません。これは Python についての有用なトリビアよりも興味深いトリビアです -- IMHO... 賭けに勝つためでしょうか?

于 2012-06-30T18:23:35.443 に答える
4

私は使用します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': []}
于 2012-06-30T18:21:28.740 に答える
2

これは厳密には辞書内包表記ではありませんが、次のとおりです。

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'), {})
于 2012-06-30T19:08:35.920 に答える