इसे हटाने के बिना सेट से एक तत्व कैसे प्राप्त करें?


427

मान लीजिए निम्नलिखित हैं:

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

sबिना कुछ किए मुझे कोई मान (कोई मान) कैसे मिलता हैs.pop() ? मैं सेट में आइटम को तब तक छोड़ना चाहता हूं जब तक मुझे यकीन नहीं हो जाता कि मैं इसे हटा सकता हूं - कुछ ऐसा जो मैं केवल दूसरे होस्ट के लिए अतुल्यकालिक कॉल के बाद सुनिश्चित कर सकता हूं।

जल्दी और गन्दी:

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

लेकिन क्या आप बेहतर तरीके से जानते हैं? आदर्श रूप से निरंतर समय में।


8
किसी को पता है कि अजगर इस समारोह को पहले से ही क्यों लागू नहीं करता है?
hlin117

उपयोग मामला क्या है? सेट में किसी कारण से यह क्षमता नहीं है। आप इसके माध्यम से पुनरावृति करना चाहते हैं और इससे संबंधित unionतत्व जैसे सेट बनाना आदि से तत्व नहीं लेते हैं। उदाहरण के लिए, यदि आप सोचते हैं कि यह बेतरतीब तत्व next(iter({3,2,1}))लौटेगा, 1तो हमेशा लौटता है । तो शायद आप सिर्फ गलत डेटा संरचना का उपयोग कर रहे हैं? उपयोग मामला क्या है?
user1685095

1
संबंधित: stackoverflow.com/questions/20625579/… (मुझे पता है, यह एक ही सवाल नहीं है, लेकिन वहाँ सार्थक विकल्प और अंतर्दृष्टि हैं।)
जॉन Y

@ hlin117 क्योंकि सेट एक अनियोजित संग्रह है । चूंकि कोई आदेश अपेक्षित नहीं है, इसलिए यह किसी भी तत्व को दिए गए स्थान पर प्राप्त करने का कोई मतलब नहीं है - यह यादृच्छिक होने की उम्मीद है।
जेकेकोमोन

जवाबों:


545

दो विकल्प जिन्हें पूरे सेट की प्रतिलिपि बनाने की आवश्यकता नहीं है:

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

या ...

e = next(iter(s))

लेकिन सामान्य तौर पर, सेट अनुक्रमण या स्लाइसिंग का समर्थन नहीं करते हैं।


4
यह मेरे सवाल का जवाब देता है। काश, मुझे लगता है कि मैं अभी भी पॉप () का उपयोग करूँगा, क्योंकि पुनरावृति तत्वों को क्रमबद्ध करने के लिए लगता है। मैं उन्हें यादृच्छिक क्रम में पसंद करूंगा ...
डैरन थॉमस

9
मुझे नहीं लगता कि iter () तत्वों को छांट रहा है - जब मैं एक सेट और पॉप () बनाता हूं, जब तक कि यह खाली न हो, मुझे सुसंगत (मेरे उदाहरण में क्रमबद्ध) मिलता है, और यह इट्रेटर - पॉप के समान होता है ) "मैं कुछ भी नहीं वादा करता हूँ" के रूप में, यादृच्छिक आदेश का वादा नहीं करता, बस मनमाना।
ब्लेयर कॉनरैड

2
+1 iter(s).next()स्थूल नहीं बल्कि महान है। किसी भी चलने योग्य वस्तु से मनमाना तत्व लेने के लिए पूरी तरह से सामान्य। आपकी पसंद अगर आप संग्रह से खाली है, तो सावधान रहना चाहते हैं।
u0b34a0f6ae

8
अगला (iter) भी ठीक है और मुझे लगता है कि यह बेहतर पढ़ता है। साथ ही, जब आप खाली हों तो केस को संभालने के लिए आप एक प्रहरी का उपयोग कर सकते हैं। जैसे अगला (iter (s), सेट ()।
जा

5
next(iter(your_list or []), None)कोई भी सेट और खाली सेट को संभालने के लिए
MrE

110

कम से कम कोड होगा:

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

जाहिर है कि यह एक नई सूची बनाएगा जिसमें सेट का प्रत्येक सदस्य शामिल हो, इसलिए यदि आपका सेट बहुत बड़ा है तो महान नहीं।


95
next(iter(s))केवल तीन वर्णों से अधिक list(s)[0]है और अन्यथा समय और अंतरिक्ष जटिलता दोनों में नाटकीय रूप से बेहतर है। तो, जबकि "कम से कम कोड" का दावा तुच्छ रूप से सच है, यह भी तुच्छ रूप से सच है कि यह सबसे खराब दृष्टिकोण है। यहां तक ​​कि मैन्युअल रूप से हटाने और फिर हटाए गए तत्व को मूल सेट में फिर से जोड़ना "पहले तत्व को निकालने के लिए एक पूरे नए कंटेनर का निर्माण करना" बेहतर है, जो वर्तमान में पागल है। मुझे इससे ज्यादा चिंता की बात यह है कि 38 स्टाॅकओवरफ्लॉवर ने वास्तव में इसे उखाड़ फेंका। मुझे पता है कि मैं इसे प्रोडक्शन कोड में देखूंगा।
सेसिल करी

19
@ उद्घाटनर: क्योंकि यह अपेक्षाकृत सरल तरीके से काम करता है। और कभी-कभी यह सब एक त्वरित स्क्रिप्ट में मायने रखता है।
tonysdg

4
@Vicrobot हाँ, लेकिन यह पूरे संग्रह की प्रतिलिपि बनाकर और O (1) ऑपरेशन को O (n) ऑपरेशन में बदल देता है। यह एक भयानक समाधान है जिसका किसी को कभी भी उपयोग नहीं करना चाहिए।
अगुरार

9
इसके अलावा अगर आप सिर्फ "कम से कम कोड" (जो गूंगा है) के लिए लक्ष्य कर रहे हैं, तो min(s)इस तरह से भयानक और अक्षम होने के दौरान भी कम वर्ण का उपयोग करता है।
अगुरार

5
कोड गोल्फ विजेता के लिए +1, जिसके पास "भयानक और अक्षम" होने के लिए मेरे पास एक व्यावहारिक काउंटरएक्सप्लिमेंट है: आकार 1 के सेटों की min(s)तुलना में थोड़ा तेज है next(iter(s)), और मुझे इस उत्तर में विशेष रूप से सेट से एकमात्र तत्व निकालने वाले विशेष मामले की तलाश है का आकार 1.
लेहिस्टर

49

मैं सोचता था कि अलग-अलग सेटों के लिए कार्य कैसे करेंगे, इसलिए मैंने एक बेंचमार्क किया:

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

मैंने इसे ऊपर के बेंचमार्क में भी शामिल किया। यह अन्य दो "तेज" समाधानों के साथ प्रतिस्पर्धा कर सकता है लेकिन अंतर बहुत अधिक नहीं है।


43

tl; डॉ

for first_item in muh_set: breakअजगर 3.x में इष्टतम दृष्टिकोण बना हुआ है। शुक्रिया, गुइडो।

यू ऐसा करो

Python 3.x समय के एक और सेट में आपका स्वागत है, जो कि wr से अलग है। उत्कृष्ट पायथन 2.x- विशिष्ट प्रतिक्रियाAChampion के समान रूप से सहायक पायथन 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.x दिनों (जिसमें मैन्युअल पुनरावृत्ति कम से कम चार बार तेज थी) से अंतराल कम हो गया है, यह मेरे में PEP 20 जोल को निराश करता है कि सबसे क्रिया समाधान सबसे अच्छा है। कम से कम सेट के पहले तत्व को निकालने के लिए एक सूची में एक सेट को परिवर्तित करना अपेक्षा के अनुसार भयानक है। गुइडो का शुक्रिया, हो सकता है कि उनका प्रकाश हमारा मार्गदर्शन करता रहे।

हैरानी की बात है, आरएनजी आधारित समाधान बिल्कुल भयानक है। सूची रूपांतरण बुरा है, लेकिन random वास्तव में भयानक सॉस केक लेता है। रैंडम नंबर भगवान के लिए इतना ।

मैं सिर्फ यह जानना चाहता हूं कि वे set.get_first()हमारे लिए पहले से ही एक विधि तैयार करेंगे। यदि आप इसे पढ़ रहे हैं, तो वे कहते हैं: "कृपया। कुछ करें।"


2
मुझे लगता है कि शिकायत next(iter(s)) दो बार की तुलना for x in s: breakमें धीमी है CPython, अजीब है। मेरा मतलब है कि है CPython। यह लगभग 50-100 बार (या ऐसा कुछ) होगा जो सी या हास्केल की तुलना में धीमी गति से एक ही काम करेगा (ज्यादातर समय, विशेष रूप से पुनरावृत्ति में, कोई पूंछ कॉल उन्मूलन और कोई अनुकूलन नहीं करता है।)। कुछ माइक्रोसेकंड को चुनने से वास्तविक अंतर नहीं पड़ता है। क्या आपको नहीं लगता? और वहाँ भी है PyPy
user1685095

39

विभिन्न दृष्टिकोणों के पीछे कुछ समय के आंकड़े प्रदान करने के लिए, निम्नलिखित कोड पर विचार करें। मिल () मेरे कस्टम के अलावा पायथन के setobject.c, तत्व को हटाए बिना सिर्फ एक पॉप () है।

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

इसका मतलब यह है कि फॉर / ब्रेक समाधान सबसे तेज़ (कभी-कभी कस्टम गेट से अधिक तेज़) समाधान है।


क्या किसी को अंदाजा है कि क्यों iter (s) .next () अन्य संभावनाओं की तुलना में इतना धीमा है, यहां तक ​​कि s.add (s.pop ()) से भी धीमा है? मेरे लिए यह बहुत खराब डिजाइन की तरह लगता है () और अगला () अगर टाइमिंग जैसा दिखता है।
peschü

अच्छी तरह से एक पंक्ति के लिए प्रत्येक पुनरावृत्ति एक नया पुनरावृत्ति वस्तु बनाता है।
रयान

3
@ रियान: क्या एक इटरेटर ऑब्जेक्ट भी नहीं बनाया गया है for x in s? "परिणाम के लिए एक पुनरावृत्त बनाया जाता है expression_list।"
मुस्तफिल

2
@ मुसिफिल यह सच है; मूल रूप से मैंने 0.14 पर "ब्रेक" को याद किया, जो वास्तव में काउंटर-सहज है। मैं समय मिलने पर इसमें एक गहरा गोता लगाना चाहता हूं।
रयान

1
मुझे पता है कि यह पुराना है, लेकिन जब s.remove()मिश्रण में iterदोनों उदाहरण जोड़ते हैं forऔर iterविनाशकारी रूप से खराब होते हैं।
एसीफम्पियन

28

चूंकि आप एक यादृच्छिक तत्व चाहते हैं, यह भी काम करेगा:

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

14
यदि आप सिर्फ एक तत्व चाहते हैं, random.choice अधिक समझदार है।
ग्रीनग लिंड

यदि आप ध्यान नहीं देते कि कौन सा तत्व लेना है, तो सूची (लि।)।
एवगेनी

8
@Gregg: आप उपयोग नहीं कर सकते choice(), क्योंकि पायथन आपके सेट को अनुक्रमित करने की कोशिश करेगा और वह काम नहीं करता है।
केविन

3
चतुर होते हुए, यह वास्तव में परिमाण के क्रम द्वारा सुझाया गया सबसे धीमा समाधान है। हाँ, यह है कि धीमी गति से। यहां तक ​​कि उस सूची के पहले तत्व को निकालने के लिए सेट को सूची में बदलना भी तेज है। हमारे बीच गैर-विश्वासियों के लिए ( ... हाय! ), इन शानदार समयों को देखें
सेसिल करी

9

मालूम होता है सबसे कॉम्पैक्ट (6 प्रतीक) हालांकि बहुत धीमी गति से जिस तरह से एक सेट तत्व (द्वारा ही संभव बनाया पाने के लिए पीईपी 3132 ):

e,*_=s

पायथन 3.5+ के साथ आप इस 7-सिंबल एक्सप्रेशन ( PEP 448 के लिए धन्यवाद ) का भी उपयोग कर सकते हैं :

[*s][0]

दोनों विकल्पों में मोटे तौर पर लूप विधि की तुलना में मेरी मशीन पर लगभग 1000 गुना धीमा है।


1
लूप विधि (या अधिक सटीक रूप से पुनरावृत्ति विधि) के लिए O (1) समय जटिलता है, जबकि ये विधियां O (N) हैं। हालांकि वे संक्षिप्त हैं । :)
फॉरएवरविंटर

6

मैं एक उपयोगिता फ़ंक्शन का उपयोग करता हूं जो मैंने लिखा था। इसका नाम कुछ भ्रामक है क्योंकि इसका मतलब है कि यह एक यादृच्छिक वस्तु या ऐसा कुछ हो सकता है।

def anyitem(iterable):
    try:
        return iter(iterable).next()
    except StopIteration:
        return None

2
आप स्याही बचाने के लिए अगली (पुनरावृति (चलने योग्य), कोई नहीं) के साथ भी जा सकते हैं :)
1 ''

3

@ लेखक के बाद। पोस्ट, मुझे इसी तरह के परिणाम मिलते हैं (पायथन 3.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()) को बदलते समय चलने योग्य उदाहरण ( for, iter) के लिए चीजें बुरी तरह से चली जाती हैं :

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

1

छोटे संग्रहों के लिए मैं आमतौर पर इस तरह के पार्सर / कन्वर्टर विधि का निर्माण करता हूं

def convertSetToList(setName):
return list(setName)

फिर मैं नई सूची का उपयोग कर सकता हूं और सूचकांक संख्या तक पहुंच सकता हूं

userFields = convertSetToList(user)
name = request.json[userFields[0]]

एक सूची के रूप में आपके पास अन्य सभी विधियां होंगी जिनके साथ आपको काम करने की आवश्यकता हो सकती है


क्यों नहीं listएक कनवर्टर विधि बनाने के बजाय का उपयोग करें ?
डैरन थॉमस

-1

कैसे के बारे में s.copy().pop()? मैंने इसे समयबद्ध नहीं किया है, लेकिन यह काम करना चाहिए और यह सरल है। यह छोटे सेट के लिए सबसे अच्छा काम करता है, क्योंकि यह पूरे सेट को कॉपी करता है।


-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 पाइथन के संस्करणों के । यह शायद सबसे अच्छा जवाब नहीं है, लेकिन यह एक और विकल्प है।

संपादित करें: आप इस तथ्य को छिपाने के लिए भी कुछ ऐसा कर सकते हैं कि आपने एक सरणी या सेट के बजाय एक तानाशाही का उपयोग किया:


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

3
यह आपके द्वारा अपेक्षित तरीके से काम नहीं करता है। अजगर 2 कुंजियों में () एक O (n) ऑपरेशन है, इसलिए अब आप निरंतर समय नहीं रहे हैं, लेकिन कम से कम कुंजियाँ [0] आपके द्वारा अपेक्षित मूल्य वापस कर देंगे। अजगर 3 चाबियाँ () में एक ओ (1) संचालन है, इसलिए याय! हालाँकि, यह अब किसी सूची ऑब्जेक्ट को नहीं लौटाता है, यह एक सेट-जैसी ऑब्जेक्ट लौटाता है जिसे अनुक्रमित नहीं किया जा सकता है, इसलिए चाबियाँ [0] टाइप टाइप को फेंक देगी। stackoverflow.com/questions/39219065/…
sage88
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.