571

次のように仮定します。

>>> s = set([1, 2, 3])

sを行わずに値 (任意の値) を取得するにはどうすればよいs.pop()ですか? アイテムを削除できると確信できるまで、アイテムをセットに残しておきたいと思います。これは、別のホストへの非同期呼び出しの後でのみ確認できます。

クイック&ダーティ:

>>> elem = s.pop()
>>> s.add(elem)

しかし、もっと良い方法を知っていますか?理想的には一定時間。

4

15 に答える 15

727

セット全体をコピーする必要のない 2 つのオプション:

for e in s:
    break
# e is now an element from s

または...

e = next(iter(s))

ただし、一般に、セットはインデックス作成またはスライスをサポートしていません。

于 2008-09-12T20:08:10.167 に答える
168

最小のコードは次のとおりです。

>>> s = set([1, 2, 3])
>>> list(s)[0]
1

明らかに、これはセットの各メンバーを含む新しいリストを作成するため、セットが非常に大きい場合はあまり役に立ちません.

于 2008-09-13T01:07:38.043 に答える
138

関数がさまざまなセットでどのように機能するのか疑問に思ったので、ベンチマークを行いました。

from random import sample

def ForLoop(s):
    for e in s:
        break
    return e

def IterNext(s):
    return next(iter(s))

def ListIndex(s):
    return list(s)[0]

def PopAdd(s):
    e = s.pop()
    s.add(e)
    return e

def RandomSample(s):
    return sample(s, 1)

def SetUnpacking(s):
    e, *_ = s
    return e

from simple_benchmark import benchmark

b = benchmark([ForLoop, IterNext, ListIndex, PopAdd, RandomSample, SetUnpacking],
              {2**i: set(range(2**i)) for i in range(1, 20)},
              argument_name='set size',
              function_aliases={first: 'First'})

b.plot()

ここに画像の説明を入力

このプロットは、一部のアプローチ ( RandomSampleSetUnpackingおよびListIndex) がセットのサイズに依存し、一般的なケースでは避けるべきであることを明確に示しています (少なくともパフォーマンス重要な場合)。他の回答ですでに示されているように、最速の方法はForLoop.

ただし、一定時間アプローチのいずれかが使用されている限り、パフォーマンスの違いはごくわずかです。


iteration_utilities(免責事項: 私は著者です) このユースケースのための便利な関数が含まれています: first:

>>> from iteration_utilities import first
>>> first({1,2,3,4})
1

上記のベンチマークにも含めました。他の 2 つの「高速」ソリューションと競合する可能性がありますが、どちらの方法でも違いはあまりありません。

于 2018-02-19T21:52:14.397 に答える
59

tl;dr

for first_item in muh_set: breakPython 3.x でも最適なアプローチです。のろいます、グイド。

あなたはこれをします

wrから推定された Python 3.x タイミングのさらに別のセットへようこそ。の優れたPython 2.x 固有の応答. AChampionの同様に役立つPython 3.x 固有の応答とは異なり、以下のタイミングは、上記で提案されたタイム アウトライアー ソリューションでもあります。

大喜びのコードスニペット

電源を入れて、チューニングして、時間を計る:

from timeit import Timer

stats = [
    "for i in range(1000): \n\tfor x in s: \n\t\tbreak",
    "for i in range(1000): next(iter(s))",
    "for i in range(1000): s.add(s.pop())",
    "for i in range(1000): list(s)[0]",
    "for i in range(1000): random.sample(s, 1)",
]

for stat in stats:
    t = Timer(stat, setup="import random\ns=set(range(100))")
    try:
        print("Time for %s:\t %f"%(stat, t.timeit(number=1000)))
    except:
        t.print_exc()

すぐに時代遅れの時代を超越したタイミング

見よ!最速のスニペットから最も遅いスニペットへの順序:

$ ./test_get.py
Time for for i in range(1000): 
    for x in s: 
        break:   0.249871
Time for for i in range(1000): next(iter(s)):    0.526266
Time for for i in range(1000): s.add(s.pop()):   0.658832
Time for for i in range(1000): list(s)[0]:   4.117106
Time for for i in range(1000): random.sample(s, 1):  21.851104

家族全員のためのフェイスプラント

当然のことながら、手動の反復は、2 番目に高速なソリューションよりも少なくとも 2 倍高速です。ギャップは Bad Old Python 2.x の時代 (手作業の反復が少なくとも 4 倍速かった) からは減少しましたが、最も冗長なソリューションが最善であるという私のPEP 20熱狂者を失望させています。少なくとも、セットの最初の要素を抽出するためだけにセットをリストに変換することは、予想どおり恐ろしいことです。グイドに感謝します。彼の光が私たちを導き続けますように。

驚くべきことに、RNG ベースのソリューションはまったくひどいものです。リストの変換は良くありませんが、random 本当に大変です。乱数の神については以上です。

アモルファスがset.get_first()すでに私たちのために方法を準備してくれることを願っています。あなたがこれを読んでいるなら、彼らは:「お願いします。何かしてください。」

于 2016-10-15T03:00:43.903 に答える
40

さまざまなアプローチの背後にあるタイミング図を提供するために、次のコードを検討してください。 get() は、Python の setobject.c への私のカスタム追加であり、要素を削除せずにただの pop() です。

from timeit import *

stats = ["for i in xrange(1000): iter(s).next()   ",
         "for i in xrange(1000): \n\tfor x in s: \n\t\tbreak",
         "for i in xrange(1000): s.add(s.pop())   ",
         "for i in xrange(1000): s.get()          "]

for stat in stats:
    t = Timer(stat, setup="s=set(range(100))")
    try:
        print "Time for %s:\t %f"%(stat, t.timeit(number=1000))
    except:
        t.print_exc()

出力は次のとおりです。

$ ./test_get.py
Time for for i in xrange(1000): iter(s).next()   :       0.433080
Time for for i in xrange(1000):
        for x in s:
                break:   0.148695
Time for for i in xrange(1000): s.add(s.pop())   :       0.317418
Time for for i in xrange(1000): s.get()          :       0.146673

これは、for/breakソリューションが最も高速であることを意味します (カスタムの get() ソリューションよりも高速な場合もあります)。

于 2009-10-23T10:47:49.297 に答える
29

ランダムな要素が必要なため、これも機能します。

>>> import random
>>> s = set([1,2,3])
>>> random.sample(s, 1)
[2]

ドキュメントには、のパフォーマンスについては言及されていないようですrandom.sample。巨大なリストと巨大なセットを使った非常に簡単な経験的テストから、リストでは一定の時間であるように見えますが、セットではそうではありません。また、セットの反復はランダムではありません。順序は未定義ですが、予測可能です:

>>> list(set(range(10))) == range(10)
True 

ランダム性が重要で、一定時間内に多数の要素 (大きなセット) が必要な場合は、random.sample最初にリストを使用して変換します。

>>> lst = list(s) # once, O(len(s))?
...
>>> e = random.sample(lst, 1)[0] # constant time
于 2008-09-12T21:43:27.363 に答える
6

私が書いたユーティリティ関数を使用します。その名前は、ランダムなアイテムまたはそのようなものである可能性があることを暗示しているため、誤解を招く可能性があります.

def anyitem(iterable):
    try:
        return iter(iterable).next()
    except StopIteration:
        return None
于 2008-09-14T04:57:51.413 に答える
4

@wr をフォローしています。投稿すると、同様の結果が得られます(Python3.5の場合)

from timeit import *

stats = ["for i in range(1000): next(iter(s))",
         "for i in range(1000): \n\tfor x in s: \n\t\tbreak",
         "for i in range(1000): s.add(s.pop())"]

for stat in stats:
    t = Timer(stat, setup="s=set(range(100000))")
    try:
        print("Time for %s:\t %f"%(stat, t.timeit(number=1000)))
    except:
        t.print_exc()

出力:

Time for for i in range(1000): next(iter(s)):    0.205888
Time for for i in range(1000): 
    for x in s: 
        break:                                   0.083397
Time for for i in range(1000): s.add(s.pop()):   0.226570

ただし、基礎となるセットを変更すると (たとえば、 への呼び出し)、反復可能な例 ( 、)remove()はうまくいきません。foriter

from timeit import *

stats = ["while s:\n\ta = next(iter(s))\n\ts.remove(a)",
         "while s:\n\tfor x in s: break\n\ts.remove(x)",
         "while s:\n\tx=s.pop()\n\ts.add(x)\n\ts.remove(x)"]

for stat in stats:
    t = Timer(stat, setup="s=set(range(100000))")
    try:
        print("Time for %s:\t %f"%(stat, t.timeit(number=1000)))
    except:
        t.print_exc()

結果:

Time for while s:
    a = next(iter(s))
    s.remove(a):             2.938494
Time for while s:
    for x in s: break
    s.remove(x):             2.728367
Time for while s:
    x=s.pop()
    s.add(x)
    s.remove(x):             0.030272
于 2016-01-24T08:38:38.487 に答える
2

値をアンパックして要素にアクセスできます。

s = set([1, 2, 3])

v1, v2, v3 = s

print(v1,v2,v3)
#1 2 3
于 2020-10-06T11:16:58.380 に答える
-6

別のオプションは、気にしない値で辞書を使用することです。例えば、


poor_man_set = {}
poor_man_set[1] = None
poor_man_set[2] = None
poor_man_set[3] = None
...

キーが単なる配列であることを除いて、キーをセットとして扱うことができます。


keys = poor_man_set.keys()
print "Some key = %s" % keys[0]

この選択の副作用は、コードが以前setのバージョンの Python と下位互換性を持つことです。最善の答えではないかもしれませんが、別の選択肢です。

編集:配列またはセットの代わりにdictを使用したという事実を隠すために、次のようなことを行うこともできます:


poor_man_set = {}
poor_man_set[1] = None
poor_man_set[2] = None
poor_man_set[3] = None
poor_man_set = poor_man_set.keys()
于 2008-09-12T20:52:06.503 に答える