如何從集合中檢索元素而不刪除它?

假設以下: html

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

如何在不執行s.pop() s狀況下從s獲取值(任何值s.pop() ? 我想把這個項留在集合中,直到我肯定我能夠刪除它 - 我只能在異步調用另外一個主機後才能肯定。 python

又快又髒: dom

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

可是你知道更好的方法嗎? 理想狀況下在恆定的時間。 異步


#1樓

要提供不一樣方法背後的一些時序數據,請考慮如下代碼。 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()

輸出是: oop

$ ./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()解決方案更快)。 性能


#2樓

關注@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()

輸出: ui

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 )的狀況很是糟糕: spa

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

#3樓

TL;博士

for first_item in muh_set: break仍然是Python 3.x中的最佳方法。 詛咒你,圭多。

你這樣作

歡迎來到另外一組Python 3.x時序,從wr推斷 特別是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

整個家庭的面部植物

不出所料, 手動迭代至少是下一個最快解決方案的兩倍 。 儘管差距已經從Bad Old Python 2.x天(其中手動迭代至少快四倍)減小,但令我失望的是PEP 20狂熱者中最詳細的解決方案是最好的。 至少將一個集合轉換爲一個列表來提取集合的第一個元素就像預期的那樣可怕。 感謝Guido,願他的光繼續引導咱們。

使人驚訝的是, 基於RNG的解決方案絕對是可怕的。 列表轉換很糟糕,但random 真的須要糟糕的蛋糕。 對於隨機數上帝來講太多了。

我只是但願他們已經爲咱們PEP了一個set.get_first()方法。 若是你正在讀這篇文章,他們會說:「請。作點什麼吧。」


#4樓

看似最緊湊 (6個符號)雖然獲取設定元素的速度很慢PEP 3132能夠實現):

e,*_=s

使用Python 3.5+,您還能夠使用此7符號表達式(感謝PEP 448 ):

[*s][0]

這兩個選項在個人機器上比for-loop方法慢大約1000倍。


#5樓

我想知道函數將如何針對不一樣的集合執行,因此我作了一個基準測試:

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()

在此輸入圖像描述

該圖清楚地代表,一些方法( RandomSampleSetUnpackingListIndex )取決於集合的大小,在通常狀況下應該避免(至少若是性能可能很重要)。 正如其餘答案所示,最快的方法是ForLoop

然而,只要使用其中一個恆定時間方法,性能差別就能夠忽略不計。


iteration_utilities (免責聲明:我是做者)包含這個用例的便利函數: first

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

我還把它包含在上面的基準測試中。 它能夠與其餘兩個「快速」解決方案競爭,但差異不大。

相關文章
相關標籤/搜索