[python] 요소를 제거하지 않고 세트에서 요소를 검색하는 방법은 무엇입니까?

다음을 가정하십시오.

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

내가 s하지 않고 어떻게 가치 (값)를 얻 s.pop()습니까? 항목을 제거 할 수있을 때까지 항목을 세트에 남겨두고 싶습니다. 다른 호스트에 대한 비동기 호출 후에 만 ​​확인할 수있는 것입니다.

빠르고 더러운 :

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

그러나 더 나은 방법을 알고 있습니까? 일정한 시간에 이상적입니다.



답변

전체 세트를 복사하지 않아도되는 두 가지 옵션 :

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

또는…

e = next(iter(s))

그러나 일반적으로 세트는 인덱싱 또는 슬라이싱을 지원하지 않습니다.


답변

가장 작은 코드는 다음과 같습니다.

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

분명히 이것은 세트의 각 멤버를 포함하는 새 목록을 생성하므로 세트가 매우 큰 경우 좋지 않습니다.


답변

함수가 다른 세트에서 어떻게 수행되는지 궁금해하여 벤치 마크를 수행했습니다.

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, SetUnpackingListIndex)이 세트의 크기에 따라 다르며 일반적인 경우 (적어도 성능 중요 할 수 있는 경우) 피해야 함을 분명히 보여줍니다 . 이미 다른 답변에서 볼 수 있듯이 가장 빠른 방법은 ForLoop입니다.

그러나 일정한 시간 접근법 중 하나를 사용하는 한 성능 차이는 무시할 수 있습니다.


iteration_utilities(면책 조항 : 저자입니다)이 사용 사례에 대한 편의 기능이 포함되어 있습니다. first:

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

또한 위의 벤치 마크에 포함 시켰습니다. 다른 두 가지 “빠른”솔루션과 경쟁 할 수 있지만 그 차이는 그리 크지 않습니다.


답변

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 배 이상 빠릅니다 . 수동 반복이 4 배 이상 빠르 던 Bad Old Python 2.x 일과의 격차가 줄어들었지만 가장 장황한 솔루션이 최고라고 PEP 20 열성에 실망했습니다 . 집합의 첫 번째 요소를 추출하기 위해 집합을 목록으로 변환하는 것은 예상만큼 끔찍합니다. 귀도에게 감사합니다. 그의 빛이 우리를 계속 인도 해 주시길 바랍니다.

놀랍게도 RNG 기반 솔루션은 끔찍합니다. 목록 변환은 좋지 않지만 random 실제로 는 끔찍한 소스 케이크가 필요합니다. 난수 신을 위해 너무 많은 .

나는 단지 비정질을 원합니다. 그들은 set.get_first()이미 우리를 위해 방법을 PEP 할 것 입니다. 이 글을 읽고 있다면, “제발. 뭔가 해봐.”


답변

서로 다른 접근 방식의 일부 타이밍 수치를 제공하려면 다음 코드를 고려하십시오.
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 () 솔루션보다 빠름).


답변

임의의 요소를 원하므로 다음과 같이 작동합니다.

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


답변

설정 요소를 얻는 데 매우 느린 방법 이지만 가장 컴팩트 한 (6 개의 기호) 겉보기 ( PEP 3132 로 가능 ) :

e,*_=s

Python 3.5 이상에서는이 7- 심볼 표현식을 사용할 수 있습니다 ( PEP 448 덕분에 ).

[*s][0]

두 옵션 모두 for-loop 방법보다 약 1000 배 느립니다.