2012-11-02 5 views
5

以下は、順序を保持しながらリスト内の重複を削除する簡単な機能です。私はそれを試して実際に動作するので、ここでの問題は私の理解です。 2回目は、指定したアイテムに対してuniq.remove(item)を実行すると、エラーが返されるようです(KeyErrorまたはValueErrorと思いますか?)そのアイテムはすでにユニークセットから削除されているためです。これは当てはまりませんか?これはエラーを発生させるはずですが、そうではないと思います。

def unique(seq): 
    uniq = set(seq) 
    return [item for item in seq if item in uniq and not uniq.remove(item)] 
+0

私は本当にこのようなコード=) – katrielalex

+5

@katrielalex - 私はしません。コレクションの削除と項目の副作用の条件を使用すると、混乱し、コードを読みにくくなります。 (IMHO) – mgilson

+1

さらに、新しい 'set'をすべて作成し、リストからフィルタとして機能するように、すべてのアイテムをポップします。私はこれがより速いとは想像もできません。そして、間違いなく明確には分かりません。(新しいデュートドリストを作成するための)単一のパスや、(リストの現在のデューピングのための)ダブルパスです。 。 –

答えて

9

アイテムが削除される前に実行されるチェックif item in uniqがあります。 andオペレータは、それが "短絡"であることがうまくいきます。これは、左の条件がFalseのように評価された場合、右の条件が評価されないことを意味します.-表現は、Trueのようにはできません。

+0

ありがとうございます。 uniq.remove(item)はどのような価値を返しますか?私は "uniq.remove(item)ではない"という全体を推測しているのは、全体をforループに変更するのではなく、リストの理解でメソッドを実行する方法ですが、なぜ私たちが使用するのかはわかりません"というよりも"この場合はちょうど "ではなく"。 "おそらくb/c unique.remove(item)はNoneまたはFalseを返しますか? – user1794459

+0

'uniq.remove(item)'は 'None'を返します。 'not None'は' True'を返します。 – mgilson

0
def unique_with_order(seq): 
    final = [] 
    for item in seq: 
     if item not in final: 
      final.append(item) 
    return final 


print unique_with_order([1,2,3,3,4,3,6]) 

これを分解して、簡単にしましょう。最近はリストの理解がすべてではありません。

+1

もちろん、すべてではありません!私達は辞書の理解と発電機の理解を持っているので: – Kos

+1

します!時には良い古き良き時代のループはちょうどいいです! –

+1

リストをユニークにする方法としては問題ありませんが、実際に表現が実際に動作する理由を概念的に理解してOPに役立つとは思いません。 – mgilson

-1

初めてこの機能を実行すると、リストの理解から[1,2,3,4]が得られ、uniqは空になります。この機能を2回目に実行すると、uniqが空になるため、[]が表示されます。 2回目の実行でエラーが発生しない理由は、Pythonのandが短絡していることです。最初の句(item in uniq)が偽であり、2番目の句を実行するのは面倒ではないことがわかります。

+0

downvoteは申し訳ありませんが、これは明らかではありません。 2回目に '[]'を得る関数を実行するとどういう意味ですか?なぜ、 'uniq'が空であるのでしょうか? – mgilson

+0

'uniq.remove(item)'が空になるので 'uniq'は空です。リストの理解は、初めて初めて短絡することはありません。私はそれを綴るために私の答えを編集します。 – dshapiro

+0

'uniq'は、関数が' uniq = set(seq) '行で呼び出されるたびに再構築されます。 – mgilson

4

set.removeは、インプレース操作です。これは何も返さないことを意味します(それはNoneを返します)。 bool(None)Falseです。

だからあなたのリストの内包が効果的にこれです:

answer = [] 
for item in seq: 
    if item in uniq and not uniq.remove(item): 
     answer.append(item) 

とPythonは条件文の短絡を(他の人が指摘したように)ないので、これは事実である:もちろん

answer = [] 
for item in seq: 
    if item in uniq: 
     if not uniq.remove(item): 
      answer.append(item) 

、以来、 unique.remove(item)NoneboolのうちいずれかがFalse)を返した場合は、両方の条件が評価されるか、どちらも評価されません。

第2の条件が存在する理由は、をuniqから削除することです。あなたは(seqで重複として)再びitemに遭遇したとき、それがuniqから、それが発見された最後の時間を削除されたため、この方法では、/場合は、それがuniqに記載されていません。今

、この変数を変更する条件として、かなり危険であることを、心に留めておくには悪いスタイル(あなたはそれが何をするかと完全に慣れていない時に、このような条件をデバッグ想像)と考えられています。条件付きの変数は、実際にチェックする変数を変更するべきではありません。したがって、変数を読み込むだけで、変数に書き込むことはできません。これはmgilsonの答え@

+0

"第2の条件の主な理由は..." - > "第2の条件の**理由のみ...":D。このような副作用の条件を使用するのは少し失礼だと考える人もいるかもしれません。 – mgilson

+0

@mgilson:Dulyが指摘した!回答が更新されました: – inspectorG4dget

+0

非常に明確な応答、ありがとうございます。 – user1794459

0

を助け

希望は正しいものですが、ここでは、あなたの情報のため、同じ機能の可能怠惰(generator)バージョンです。つまり、要素のセットが存在する限り、メモリに収まらないイテラブル(無限のイテレータを含む)で動作します。

def unique(iterable): 
    uniq = set() 
    for item in iterable: 
     if item not in uniq: 
      uniq.add(item) 
      yield item 
1

mgilsonと他の人がこの質問にきちんと答えてくれました。私は、以下に引用される、すなわちitertoolsドキュメントのrecipe sectionからunique_everseenレシピを使用して、私はおそらくpythonでこれを行う標準的な方法です何か指摘するかもしれないと思った:

from itertools import ifilterfalse 

def unique_everseen(iterable, key=None): 
    "List unique elements, preserving order. Remember all elements ever seen." 
    # unique_everseen('AAAABBBCCDAABBB') --> A B C D 
    # unique_everseen('ABBCcAD', str.lower) --> A B C D 
    seen = set() 
    seen_add = seen.add 
    if key is None: 
     for element in ifilterfalse(seen.__contains__, iterable): 
      seen_add(element) 
      yield element 
    else: 
     for element in iterable: 
      k = key(element) 
      if k not in seen: 
       seen_add(k) 
       yield element 
関連する問題