假設以下: html
>>> s = set([1, 2, 3])
如何在不執行s.pop()
s
狀況下從s
獲取值(任何值s.pop()
? 我想把這個項留在集合中,直到我肯定我能夠刪除它 - 我只能在異步調用另外一個主機後才能肯定。 python
又快又髒: dom
>>> elem = s.pop() >>> s.add(elem)
可是你知道更好的方法嗎? 理想狀況下在恆定的時間。 異步
要提供不一樣方法背後的一些時序數據,請考慮如下代碼。 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()解決方案更快)。 性能
關注@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()
)時,可迭代示例( for
, iter
)的狀況很是糟糕: 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
for first_item in muh_set: break
仍然是Python 3.x中的最佳方法。 詛咒你,圭多。
歡迎來到另外一組Python 3.x時序,從wr推斷。 特別是Python 2.x特有的響應 。 與AChampion一樣有用的Python 3.x特定響應不一樣 ,下面的時間安排也是上面提出的時間異常解決方案 - 包括:
list(s)[0]
, John的新穎的基於序列的解決方案 。 random.sample(s, 1)
, dF。 不拘一格的基於RNG的解決方案 。 打開,收聽,計時:
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()
方法。 若是你正在讀這篇文章,他們會說:「請。作點什麼吧。」
看似最緊湊 (6個符號)雖然獲取設定元素的速度很慢 ( PEP 3132能夠實現):
e,*_=s
使用Python 3.5+,您還能夠使用此7符號表達式(感謝PEP 448 ):
[*s][0]
這兩個選項在個人機器上比for-loop方法慢大約1000倍。
我想知道函數將如何針對不一樣的集合執行,因此我作了一個基準測試:
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()
該圖清楚地代表,一些方法( RandomSample
, SetUnpacking
和ListIndex
)取決於集合的大小,在通常狀況下應該避免(至少若是性能可能很重要)。 正如其餘答案所示,最快的方法是ForLoop
。
然而,只要使用其中一個恆定時間方法,性能差別就能夠忽略不計。
iteration_utilities
(免責聲明:我是做者)包含這個用例的便利函數: first
:
>>> from iteration_utilities import first >>> first({1,2,3,4}) 1
我還把它包含在上面的基準測試中。 它能夠與其餘兩個「快速」解決方案競爭,但差異不大。