Załóżmy, że:
>>> s = set([1, 2, 3])
Jak uzyskać wartość (dowolną wartość) s
bez robienia s.pop()
? Chcę pozostawić element w zestawie, dopóki nie będę pewien, że mogę go usunąć - czego mogę być pewien tylko po asynchronicznym wywołaniu do innego hosta.
Szybko i brudno:
>>> elem = s.pop()
>>> s.add(elem)
Ale czy znasz lepszy sposób? Idealnie w stałym czasie.
union
etc, nie pobierając z niego elementów. Na przykładnext(iter({3,2,1}))
zawsze wraca,1
więc jeśli myślałeś, że zwróci losowy element - nie będzie. Więc może po prostu używasz niewłaściwej struktury danych? Jaki jest przypadek użycia?Odpowiedzi:
Dwie opcje, które nie wymagają kopiowania całego zestawu:
Lub...
Ogólnie jednak zestawy nie obsługują indeksowania ani krojenia.
źródło
iter(s).next()
nie jest obrzydliwe, ale świetne. Całkowicie ogólne pobieranie dowolnych elementów z dowolnego obiektu iterowalnego. Twój wybór, jeśli chcesz zachować ostrożność, jeśli kolekcja jest pusta.next(iter(your_list or []), None)
obsłużyć Brak zestawów i puste zestawyNajmniejszy kod to:
Oczywiście stworzyłoby to nową listę, która zawiera każdego członka zestawu, więc nie jest świetnie, jeśli twój zestaw jest bardzo duży.
źródło
next(iter(s))
przekracza tylkolist(s)[0]
przez trzy znaki i jest inaczej dramatycznie lepszy zarówno w czasie i przestrzeni złożoności. Tak więc, chociaż twierdzenie o „najmniejszym kodzie” jest trywialnie prawdziwe, jest również trywialnie prawdziwe, że jest to najgorsze możliwe podejście. Nawet ręczne usunięcie, a następnie ponowne dodanie usuniętego elementu do oryginalnego zestawu jest lepsze niż „zbudowanie całego nowego kontenera tylko w celu wyodrębnienia pierwszego elementu”, co jest ewidentnie szalone. Bardziej mnie martwi fakt, że 38 Stackoverflowers faktycznie to poparło. Wiem tylko, że zobaczę to w kodzie produkcyjnym.min(s)
używa nawet mniejszej liczby znaków, będąc jednocześnie tak strasznym i nieefektywnym jak to.min(s)
jest nieco szybszy niżnext(iter(s))
dla zestawów rozmiaru 1, i doszedłem do tej odpowiedzi, szczególnie szukając specjalnego przypadku wydobywania jedynego elementu z zestawów w rozmiarze 1.Zastanawiałem się, jak te funkcje będą działać dla różnych zestawów, więc zrobiłem test porównawczy:
Ten wykres pokazuje wyraźnie, że niektóre podejścia (
RandomSample
,SetUnpacking
iListIndex
) zależy od wielkości zestawu i należy ich unikać w przypadku ogólnym (przynajmniej jeśli wydajność może być ważne). Jak już pokazują inne odpowiedzi, najszybszym sposobem jestForLoop
.Jednak dopóki stosowane jest jedno ze stałych czasów, różnica wydajności będzie nieznaczna.
iteration_utilities
(Uwaga: Jestem autorem) zawiera funkcję wygody dla tego przypadku użyciafirst
:Umieściłem go również w powyższym teście. Może konkurować z pozostałymi dwoma „szybkimi” rozwiązaniami, ale różnica nie jest duża.
źródło
tl; dr
for first_item in muh_set: break
pozostaje optymalnym podejściem w Pythonie 3.x. Przeklnij cię, Guido.ty to zrób
Witaj w innym zestawie taktowania Python 3.x, ekstrapolowanym z wr. „S doskonałe Pythona 2.x-swoistych . W przeciwieństwie do równie pomocnej odpowiedzi AChampiona specyficznej dla Python 3.x , poniższe czasy również sugerują rozwiązania odstające od czasu - w tym:
list(s)[0]
, Nowatorskie rozwiązanie oparte na sekwencji Johna .random.sample(s, 1)
, dF. eklektyczne rozwiązanie oparte na RNG .Fragmenty kodu dla Wielkiej Radości
Włącz, nastrój, czas:
Szybko przestarzałe ponadczasowe czasy
Ujrzeć! Uporządkowane przez najszybsze do najwolniejszych fragmentów:
Osłony na twarz dla całej rodziny
Nic dziwnego, że ręczna iteracja pozostaje co najmniej dwa razy szybsza niż następne najszybsze rozwiązanie. Chociaż różnica zmniejszyła się od dni Bad Old Python 2.x (w których manualna iteracja była co najmniej cztery razy szybsza), rozczarowuje mnie fanatyka PEP 20 , że najbardziej szczegółowe rozwiązanie jest najlepsze. Przynajmniej przekształcenie zestawu w listę tylko w celu wyodrębnienia pierwszego elementu zestawu jest tak okropne, jak oczekiwano. Dzięki Guido, niech jego światło nadal nas prowadzi.
Co zaskakujące, rozwiązanie oparte na RNG jest absolutnie okropne. Konwersja list jest zła, ale
random
tak naprawdę wymaga okropnego sosu. Tyle o losowym Bogu liczb .Chciałbym tylko, żeby amorficzni już odkryli
set.get_first()
dla nas metodę. Jeśli to czytasz, oni: „Proszę. Zrób coś”.źródło
next(iter(s))
jest dwa razy wolniejsze niżfor x in s: break
w,CPython
jest trochę dziwne. Mam na myśli, że takCPython
. Będzie to około 50-100 razy (lub coś w tym rodzaju) wolniej niż C lub Haskell robiąc to samo (przez większość czasu, szczególnie w iteracji, bez eliminacji ogona i żadnych optymalizacji). Utrata niektórych mikrosekund nie robi żadnej różnicy. Nie sądzisz? Jest też PyPyAby podać wartości czasowe różnych podejść, rozważ poniższy kod. Get () jest moim niestandardowym dodatkiem do setobject.c Pythona, który jest tylko pop () bez usuwania elementu.
Dane wyjściowe to:
Oznacza to, że rozwiązanie for / break jest najszybsze (czasem szybsze niż niestandardowe rozwiązanie get ()).
źródło
for x in s
? „Tworzony jest iterator dla wynikuexpression_list
.”s.remove()
do mieszanki tychiter
przykładów zarównofor
iiter
iść katastrofalnie źle.Ponieważ chcesz elementu losowego, będzie to również działać:
Dokumentacja nie wspomina o wydajności
random.sample
. Z naprawdę szybkiego testu empirycznego z ogromną listą i ogromnym zestawem wydaje się, że jest to stały czas na listę, ale nie na zestaw. Również iteracja na zbiorze nie jest przypadkowa; kolejność jest niezdefiniowana, ale przewidywalna:Jeśli losowość jest ważna i potrzebujesz kilku elementów w stałym czasie (duże zestawy), najpierw użyłbym
random.sample
i przekonwertował na listę:źródło
choice()
, ponieważ Python spróbuje zaindeksować twój zestaw i to nie działa.Pozornie najbardziej kompaktowy (6 symboli), ale bardzo powolny sposób na uzyskanie zestawu elementów (możliwe dzięki PEP 3132 ):
W Pythonie 3.5+ możesz także użyć tego 7-symbolowego wyrażenia (dzięki PEP 448 ):
Obie opcje są około 1000 razy wolniejsze na mojej maszynie niż metoda for-loop.
źródło
Używam napisanej przeze mnie funkcji narzędziowej. Jego nazwa jest nieco myląca, ponieważ sugeruje, że może to być losowy przedmiot lub coś w tym rodzaju.
źródło
Obserwowanie @wr. post, otrzymuję podobne wyniki (dla Python3.5)
Wynik:
Jednak przy zmianie zestawu podstawowego (np. Wywołanie do
remove()
) sprawy idą źle w przypadku iterowalnych przykładów (for
,iter
):Prowadzi do:
źródło
To, co zwykle robię dla małych kolekcji, to stworzenie takiej metody parsera / konwertera
Następnie mogę skorzystać z nowej listy i uzyskać dostęp według numeru indeksu
Na liście znajdziesz wszystkie inne metody, z którymi możesz potrzebować pracować
źródło
list
zamiast tworzenia metody konwertera?Jak o
s.copy().pop()
? Nie mierzyłem czasu, ale powinno działać i jest proste. Działa najlepiej w przypadku małych zestawów, ponieważ kopiuje cały zestaw.źródło
Inną opcją jest użycie słownika z wartościami, na których ci nie zależy. Na przykład,
Możesz traktować klucze jako zestaw, z wyjątkiem tego, że są tylko tablicą:
Efektem ubocznym tego wyboru jest to, że twój kod będzie wstecznie kompatybilny ze starszymi, wcześniejszymi
set
wersjami Pythona. Być może nie jest to najlepsza odpowiedź, ale to kolejna opcja.Edycja: Możesz nawet zrobić coś takiego, aby ukryć fakt, że użyłeś dykta zamiast tablicy lub zestawu:
źródło