"1000000000000000 रेंज में (1000000000000001)" पायथन 3 में इतना तेज क्यों है?


2111

यह मेरी समझ है कि range()फ़ंक्शन, जो वास्तव में पायथन 3 में एक ऑब्जेक्ट प्रकार है , जनरेटर के समान मक्खी पर अपनी सामग्री उत्पन्न करता है।

यह मामला होने के नाते, मुझे उम्मीद है कि निम्न पंक्ति समय की एक विषम राशि लेगी, क्योंकि यह निर्धारित करने के लिए कि क्या 1 क्वाड्रिलियन सीमा में है, एक क्वाडरिलिन मान उत्पन्न करना होगा:

1000000000000000 in range(1000000000000001)

इसके अलावा: ऐसा लगता है कि कोई फर्क नहीं पड़ता कि मैं कितने शून्य जोड़ता हूं, गणना कम या ज्यादा समय (मूल रूप से तात्कालिक) समान समय लेती है।

मैंने इस तरह की चीजों की भी कोशिश की है, लेकिन गणना अभी भी लगभग तत्काल है:

1000000000000000000000 in range(0,1000000000000000000001,10) # count by tens

अगर मैं अपने स्वयं के रेंज फ़ंक्शन को लागू करने का प्रयास करता हूं, तो परिणाम इतना अच्छा नहीं है !!

def my_crappy_range(N):
    i = 0
    while i < N:
        yield i
        i += 1
    return

range()हुड के नीचे क्या वस्तु है जो इसे इतनी तेजी से बनाती है?


मार्टिज़न पीटरर्स का जवाब इसकी पूर्णता के लिए चुना गया था, लेकिन पायथन 3 में पूर्ण अनुक्रम होने के लिए इसका क्या मतलब है , और इसकी कुछ जानकारी / चेतावनी पायथन कार्यान्वयन के लिए फ़ंक्शन अनुकूलन के लिए संभावित झुकाव के बारे में कुछ जानकारी / चेतावनी के बारे में चर्चा करने के लिए एब्नर्ट का पहला उत्तर भी देखें। । एब्नर्ट का अन्य उत्तर कुछ और विस्तार में जाता है और पायथन 3 में अनुकूलन के पीछे के इतिहास में रुचि रखने वालों के लिए लिंक प्रदान करता है (और पायथन 2 में अनुकूलन के अभाव में )। पोक द्वारा और वाइम के उत्तर प्रासंगिक सी स्रोत कोड और रुचि रखने वालों के लिए स्पष्टीकरण प्रदान करते हैं।range__contains__xrange


70
ध्यान दें कि यह केवल तभी होता है जब हम जिस वस्तु की जांच कर रहे हैं वह एक boolया longप्रकार है, अन्य वस्तु प्रकारों के साथ यह पागल हो जाएगा। के साथ कोशिश करें:100000000000000.0 in range(1000000000000001)
अश्विनी चौधरी

10
आपको किसने बताया कि rangeजनरेटर है?
अचानक

7
@abarnert मुझे लगता है कि मैंने जो संपादन किया है वह भ्रम बरकरार है।
रिक

5
@ अश्विनीचौधरी पायथन 2 पायथन 3 xrangeके समानrange नहीं है ?
शानदार

28
@ सुपरबाइक xrange()ऑब्जेक्ट्स की कोई __contains__विधि नहीं है, इसलिए आइटम चेक को सभी आइटमों के माध्यम से लूप करना पड़ता है। प्लस वहाँ में कुछ अन्य परिवर्तन कर रहे हैं range(), जैसे कि यह टुकड़ा करने की क्रिया का समर्थन करता है (जो फिर से एक रिटर्न rangeभी वस्तु) और अब है countऔर indexतरीकों के साथ संगत बनाने के लिए collections.Sequenceएबीसी।
अश्विनी चौधरी

जवाबों:


2167

पायथन 3 range()ऑब्जेक्ट तुरंत संख्या उत्पन्न नहीं करता है; यह एक स्मार्ट सीक्वेंस ऑब्जेक्ट है जो मांग पर संख्या पैदा करता है । इसमें सभी शामिल हैं आपकी शुरुआत, रोकें और कदम मान, फिर जब आप ऑब्जेक्ट पर पुनरावृति करते हैं तो अगले पूर्णांक को प्रत्येक पुनरावृत्ति की गणना की जाती है।

ऑब्जेक्ट object.__contains__हुक को भी लागू करता है , और गणना करता है कि क्या आपका नंबर इसकी सीमा का हिस्सा है। गणना एक (निकट) निरंतर समय ऑपरेशन * है । रेंज में सभी संभव पूर्णांकों के माध्यम से स्कैन करने की आवश्यकता नहीं है।

से range()वस्तु प्रलेखन :

rangeएक नियमित रूप से टाइप का लाभ listया tupleयह है कि एक रेंज ऑब्जेक्ट हमेशा एक ही (छोटी) मेमोरी की मात्रा लेगा, इससे कोई फर्क नहीं पड़ता कि यह किस रेंज का प्रतिनिधित्व करता है (क्योंकि यह केवल स्टोर करता है start, stopऔर stepमान, व्यक्तिगत आइटम और सबरेंज की गणना करता है। जैसी जरूरत थी)।

तो कम से कम, आपकी range()वस्तु क्या करेगी:

class my_range(object):
    def __init__(self, start, stop=None, step=1):
        if stop is None:
            start, stop = 0, start
        self.start, self.stop, self.step = start, stop, step
        if step < 0:
            lo, hi, step = stop, start, -step
        else:
            lo, hi = start, stop
        self.length = 0 if lo > hi else ((hi - lo - 1) // step) + 1

    def __iter__(self):
        current = self.start
        if self.step < 0:
            while current > self.stop:
                yield current
                current += self.step
        else:
            while current < self.stop:
                yield current
                current += self.step

    def __len__(self):
        return self.length

    def __getitem__(self, i):
        if i < 0:
            i += self.length
        if 0 <= i < self.length:
            return self.start + i * self.step
        raise IndexError('Index out of range: {}'.format(i))

    def __contains__(self, num):
        if self.step < 0:
            if not (self.stop < num <= self.start):
                return False
        else:
            if not (self.start <= num < self.stop):
                return False
        return (num - self.start) % self.step == 0

यह अभी भी कई चीजें हैं जो एक वास्तविक याद आ रही है range()(जैसे कि समर्थन करता है .index()या .count()तरीकों, हैशिंग, समानता परीक्षण, या टुकड़ा करने की क्रिया), लेकिन आप एक विचार देना चाहिए।

मैंने __contains__पूर्णांक परीक्षणों पर ध्यान केंद्रित करने के लिए कार्यान्वयन को भी सरल बनाया ; यदि आप एक वास्तविक range()वस्तु को एक गैर-पूर्णांक मान (उपवर्ग सहित int) देते हैं, तो एक धीमा स्कैन यह देखने के लिए शुरू किया जाता है कि क्या कोई मिलान है, जैसे कि आप सभी निहित मूल्यों की सूची के खिलाफ एक परीक्षण परीक्षण का उपयोग करते हैं। यह अन्य संख्यात्मक प्रकारों का समर्थन जारी रखने के लिए किया गया था जो कि पूर्णांक के साथ समानता परीक्षण का समर्थन करने के लिए होता है, लेकिन पूर्णांक अंकगणित के भी समर्थन की उम्मीद नहीं की जाती है। मूल पायथन मुद्दे को देखें, जिसमें रोकथाम परीक्षण लागू किया गया था।


* निरंतर समय के पास क्योंकि पायथन पूर्णांक अनबाउंड हैं और इसलिए समय के बढ़ने के साथ-साथ गणित का संचालन भी बढ़ता जाता है, जिससे यह O (लॉग एन) ऑपरेशन बन जाता है। चूंकि यह सब 30-बिट विखंडन में अनुकूलित C कोड और पायथन स्टोर पूर्णांक मानों में निष्पादित किया गया है, इससे पहले कि आप इसमें शामिल पूर्णांकों के आकार के कारण किसी भी प्रदर्शन प्रभाव को देखने से पहले स्मृति से बाहर निकल जाएंगे।


58
मजेदार तथ्य: क्योंकि आपके पास काम करने का कार्यान्वयन है __getitem__और __len__, __iter__कार्यान्वयन वास्तव में अनावश्यक है।
लूसट्रिएल

2
@ ल्यूक्रिएल: पायथन 2.3 में , एक विशेष xrangeiteratorरूप से विशेष रूप से जोड़ा गया था , क्योंकि यह काफी तेज नहीं था। और फिर 3.x में (मुझे यकीन नहीं है कि अगर यह 3.0 या 3.2 था) तो इसे फेंक दिया गया था और वे उसी listiteratorप्रकार का listउपयोग करते हैं जो उपयोग करता है।
अचानक

1
मैं कंस्ट्रक्टर को परिभाषित करता हूं def __init__(self, *start_stop_step)और इसे वहां से पार्स करता हूं; अब जिस तरह से तर्कों को लेबल किया जाता है वह अब भ्रमित करने वाला है। फिर भी, +1; आप अभी भी निश्चित रूप से व्यवहार समझाया।
कोडी पियर्सल

1
@CodyPiersall: दुर्भाग्य से, यह वास्तविक वर्ग के शुरुआती के हस्ताक्षर है। rangeसे अधिक पुराना है *args(बहुत कम argclinicAPI जो C-API फ़ंक्शन को पूर्ण पायथन हस्ताक्षर देता है)। कुछ अन्य पुराने फ़ंक्शंस (और कुछ नए फ़ंक्शंस, जैसे xrange, sliceऔर, और itertools.isliceस्थिरता के लिए) एक ही तरह से काम करते हैं, लेकिन अधिकांश भाग के लिए, गुइडो और बाकी कोर देवता आपसे सहमत हैं। 2.0+ डॉक्स भी वर्णन करते हैं rangeऔर दोस्तों के रूप में यदि वे सी + + स्टाइल ओवरलोड थे, बजाय वास्तविक भ्रमित हस्ताक्षर दिखाने के।
abarnert

2
@CodyPiersall: वास्तव में, यहां गुइडो की argclinicचर्चा का एक उद्धरण है , जब निक कॉघलान को अस्पष्ट तरीके से परिभाषित करने की अनुमति देने का एक तरीका आया range: "कृपया लोगों के लिए मेरे सबसे खराब डिजाइन निर्णय की प्रतिलिपि बनाना आसान नहीं है।" इसलिए, मुझे पूरा यकीन है कि वह इस बात rangeसे सहमत हैं कि लिखित रूप में भ्रमित है।
abarnert

843

यहां मूलभूत गलतफहमी यह है कि rangeएक जनरेटर है। यह। वास्तव में, यह किसी भी प्रकार का पुनरावृत्ति नहीं है।

आप इसे बहुत आसानी से बता सकते हैं:

>>> a = range(5)
>>> print(list(a))
[0, 1, 2, 3, 4]
>>> print(list(a))
[0, 1, 2, 3, 4]

यदि यह एक जनरेटर था, तो इसे एक बार पुनरावृत्त करने से यह समाप्त हो जाएगा:

>>> b = my_crappy_range(5)
>>> print(list(b))
[0, 1, 2, 3, 4]
>>> print(list(b))
[]

rangeवास्तव में क्या है, एक अनुक्रम है, बस एक सूची की तरह। आप इसका परीक्षण भी कर सकते हैं:

>>> import collections.abc
>>> isinstance(a, collections.abc.Sequence)
True

इसका मतलब यह है कि इसे अनुक्रम होने के सभी नियमों का पालन करना है:

>>> a[3]         # indexable
3
>>> len(a)       # sized
5
>>> 3 in a       # membership
True
>>> reversed(a)  # reversible
<range_iterator at 0x101cd2360>
>>> a.index(3)   # implements 'index'
3
>>> a.count(3)   # implements 'count'
1

एक rangeऔर एक के बीच का अंतर यह listहै कि rangeएक आलसी या गतिशील अनुक्रम है; यह अपने मूल्यों के सभी याद नहीं है, यह सिर्फ याद करते हैं इसके start, stop, और step, और पर मांग पर मूल्यों बनाता है __getitem__

(साइड नोट के रूप में, यदि आप print(iter(a)), आप rangeउसी listiteratorप्रकार का उपयोग करने वाले नोटिस करेंगे list। वह कैसे काम करता है? इस तथ्य को छोड़कर listiteratorकोई विशेष उपयोग नहीं करता listहै कि यह C का कार्यान्वयन प्रदान करता है __getitem__, इसलिए यह ठीक काम करता है rangeभी।)


अब, ऐसा कुछ भी नहीं है जो कहता है कि Sequence.__contains__निरंतर समय होना चाहिए - वास्तव में, अनुक्रमों के स्पष्ट उदाहरणों के लिए list, ऐसा नहीं है। लेकिन ऐसा कुछ नहीं है जो कहता है कि यह नहीं हो सकता है। और इसे लागू range.__contains__करने के लिए इसे गणितीय रूप से जांचना आसान है ( (val - start) % stepलेकिन, नकारात्मक चरणों से निपटने के लिए कुछ अतिरिक्त जटिलता के साथ) वास्तव में सभी मूल्यों को उत्पन्न करने और परीक्षण करने की तुलना में, इसलिए इसे बेहतर तरीके से क्यों नहीं करना चाहिए ?

लेकिन भाषा में ऐसा कुछ भी नहीं प्रतीत होता है जो इसकी गारंटी देता हो। जैसा कि अश्विनी चौधरी बताते हैं, यदि आप इसे पूर्णांक में परिवर्तित करने और गणितीय परीक्षण करने के बजाय इसे एक गैर-अभिन्न मूल्य देते हैं, तो यह सभी मूल्यों को पुनरावृत्त करने और एक-एक करके उनकी तुलना करने के लिए वापस आ जाएगा। और सिर्फ इसलिए कि CPython 3.2+ और PyPy 3.x संस्करणों में यह अनुकूलन होता है, और यह एक स्पष्ट अच्छा विचार है और ऐसा करना आसान है, कोई कारण नहीं है कि IronPython या NewKickAssPython 3.x इसे छोड़ नहीं सकता। (और वास्तव में CPython 3.0-3.1 में यह शामिल नहीं था ।)


यदि rangeवास्तव में एक जनरेटर थे, जैसे my_crappy_range, तो यह __contains__इस तरह से परीक्षण करने के लिए समझ में नहीं आएगा , या कम से कम जिस तरह से यह समझ में आता है वह स्पष्ट नहीं होगा। यदि आप पहले से ही पहले 3 मानों की पुनरावृत्ति कर चुके हैं, तो क्या 1अभी भी inजनरेटर है? क्या 1इसे पुनरावृत्त करने के लिए परीक्षण करना चाहिए और 1(या पहले मूल्य तक >= 1) सभी मूल्यों का उपभोग करना चाहिए ?


10
यह सीधे पाने के लिए एक बहुत महत्वपूर्ण बात है। मुझे लगता है कि पायथन 2 और 3 के बीच अंतर इस बिंदु पर मेरी उलझन पैदा कर सकता है। किसी भी मामले में, मुझे एहसास हुआ होना चाहिए था के बाद से range(के साथ सूचीबद्ध है listऔर tupleएक दृश्य प्रकार के रूप में)
रिक

4
@ रिकेटी: वास्तव में, 2.6+ में (मुझे लगता है; शायद 2.5+), xrangeएक क्रम भी है। 2.7 डॉक्स देखें । वास्तव में, यह लगभग एक अनुक्रम था।
मई'15

5
@ रिकेटी: वास्तव में, मैं गलत था; 2.6-2.7 (और 3.0-3.1) में, यह एक अनुक्रम होने का दावा करता है, लेकिन यह अभी भी लगभग एक अनुक्रम है। मेरा दूसरा जवाब देखिए।
abarnert

2
यह एक पुनरावृत्त नहीं है, यह एक अनुक्रम है (जावा के संदर्भ में Iterable, C # के IEnumerable) - एक .__iter__()पद्धति के साथ कुछ ऐसा जो पुनरावृत्त लौटेगा। इसका उपयोग केवल एक बार किया जा सकता है।
स्मित जॉन्थ

4
@ThomasAhle: क्योंकि rangeपूर्णांक नहीं होने पर प्रकार की जाँच नहीं करता है, क्योंकि यह हमेशा संभव है __eq__कि एक प्रकार के साथ संगत हो int। निश्चित strरूप से , स्पष्ट रूप से काम नहीं करेगा, लेकिन वे सभी प्रकारों की स्पष्ट रूप से जांच करके चीजों को धीमा नहीं करना चाहते थे जो वहां नहीं हो सकते हैं (और आखिरकार, एक strउपवर्ग ओवरराइड कर सकता है __eq__और उसमें समाहित हो सकता है range)।
शैडो रेंजर

377

स्रोत का उपयोग करें , ल्यूक!

CPython में, range(...).__contains__(एक विधि आवरण) अंततः एक साधारण गणना में आएगी जो यह जांचती है कि मान संभवतः सीमा में हो सकता है। यहां गति का कारण हम सीमा वस्तु के प्रत्यक्ष पुनरावृत्ति के बजाय सीमा के बारे में गणितीय तर्क का उपयोग कर रहे हैं । प्रयुक्त तर्क की व्याख्या करने के लिए:

  1. जाँच लें कि संख्या startऔर के बीच है stop, और
  2. जाँचें कि स्ट्राइड वैल्यू हमारी संख्या को "स्टेप ओवर" नहीं करता है।

उदाहरण के लिए, 994में है range(4, 1000, 2)क्योंकि:

  1. 4 <= 994 < 1000, तथा
  2. (994 - 4) % 2 == 0

पूर्ण सी कोड नीचे शामिल किया गया है, जो स्मृति प्रबंधन और संदर्भ गणना विवरण के कारण थोड़ा अधिक क्रिया है, लेकिन मूल विचार यह है:

static int
range_contains_long(rangeobject *r, PyObject *ob)
{
    int cmp1, cmp2, cmp3;
    PyObject *tmp1 = NULL;
    PyObject *tmp2 = NULL;
    PyObject *zero = NULL;
    int result = -1;

    zero = PyLong_FromLong(0);
    if (zero == NULL) /* MemoryError in int(0) */
        goto end;

    /* Check if the value can possibly be in the range. */

    cmp1 = PyObject_RichCompareBool(r->step, zero, Py_GT);
    if (cmp1 == -1)
        goto end;
    if (cmp1 == 1) { /* positive steps: start <= ob < stop */
        cmp2 = PyObject_RichCompareBool(r->start, ob, Py_LE);
        cmp3 = PyObject_RichCompareBool(ob, r->stop, Py_LT);
    }
    else { /* negative steps: stop < ob <= start */
        cmp2 = PyObject_RichCompareBool(ob, r->start, Py_LE);
        cmp3 = PyObject_RichCompareBool(r->stop, ob, Py_LT);
    }

    if (cmp2 == -1 || cmp3 == -1) /* TypeError */
        goto end;
    if (cmp2 == 0 || cmp3 == 0) { /* ob outside of range */
        result = 0;
        goto end;
    }

    /* Check that the stride does not invalidate ob's membership. */
    tmp1 = PyNumber_Subtract(ob, r->start);
    if (tmp1 == NULL)
        goto end;
    tmp2 = PyNumber_Remainder(tmp1, r->step);
    if (tmp2 == NULL)
        goto end;
    /* result = ((int(ob) - start) % step) == 0 */
    result = PyObject_RichCompareBool(tmp2, zero, Py_EQ);
  end:
    Py_XDECREF(tmp1);
    Py_XDECREF(tmp2);
    Py_XDECREF(zero);
    return result;
}

static int
range_contains(rangeobject *r, PyObject *ob)
{
    if (PyLong_CheckExact(ob) || PyBool_Check(ob))
        return range_contains_long(r, ob);

    return (int)_PySequence_IterSearch((PyObject*)r, ob,
                                       PY_ITERSEARCH_CONTAINS);
}

विचार का "मांस" लाइन में उल्लिखित है :

/* result = ((int(ob) - start) % step) == 0 */ 

अंतिम नोट के रूप में - range_containsकोड स्निपेट के निचले भाग में फ़ंक्शन को देखें। यदि सटीक प्रकार की जांच विफल हो जाती है, तो हम वर्णित चतुर एल्गोरिथ्म का उपयोग नहीं करते हैं, इसके बजाय रेंज का उपयोग करते हुए एक गूढ़ पुनरावृत्ति खोज पर वापस आते हैं _PySequence_IterSearch! आप दुभाषिया में इस व्यवहार की जाँच कर सकते हैं (मैं यहाँ v3.5.0 का उपयोग कर रहा हूँ):

>>> x, r = 1000000000000000, range(1000000000000001)
>>> class MyInt(int):
...     pass
... 
>>> x_ = MyInt(x)
>>> x in r  # calculates immediately :) 
True
>>> x_ in r  # iterates for ages.. :( 
^\Quit (core dumped)

144

मार्टिज़न के उत्तर में जोड़ने के लिए, यह स्रोत का प्रासंगिक हिस्सा है (सी में, जैसा कि रेंज ऑब्जेक्ट देशी कोड में लिखा गया है):

static int
range_contains(rangeobject *r, PyObject *ob)
{
    if (PyLong_CheckExact(ob) || PyBool_Check(ob))
        return range_contains_long(r, ob);

    return (int)_PySequence_IterSearch((PyObject*)r, ob,
                                       PY_ITERSEARCH_CONTAINS);
}

तो PyLongवस्तुओं के लिए (जो intपायथन 3 में है), यह range_contains_longपरिणाम निर्धारित करने के लिए फ़ंक्शन का उपयोग करेगा । और यह फ़ंक्शन अनिवार्य रूप से जांचता है कि obक्या निर्दिष्ट सीमा में है (हालांकि यह सी में थोड़ा अधिक जटिल दिखता है)।

यदि यह एक intवस्तु नहीं है , तो यह तब तक पुनरावृत्ति करता है जब तक कि यह मूल्य नहीं पाता (या नहीं)।

पूरे तर्क को इस तरह से छद्म-पायथन में अनुवाद किया जा सकता है:

def range_contains (rangeObj, obj):
    if isinstance(obj, int):
        return range_contains_long(rangeObj, obj)

    # default logic by iterating
    return any(obj == x for x in rangeObj)

def range_contains_long (r, num):
    if r.step > 0:
        # positive step: r.start <= num < r.stop
        cmp2 = r.start <= num
        cmp3 = num < r.stop
    else:
        # negative step: r.start >= num > r.stop
        cmp2 = num <= r.start
        cmp3 = r.stop < num

    # outside of the range boundaries
    if not cmp2 or not cmp3:
        return False

    # num must be on a valid step inside the boundaries
    return (num - r.start) % r.step == 0

11
@ क्रिसहेलिंग: मुझे लगता है कि यह अलग-अलग-पर्याप्त जानकारी (और इसके बारे में पर्याप्त) है कि मार्टिज़न के उत्तर को संपादित करना यहां उचित नहीं होगा। यह एक निर्णय कॉल है, लेकिन लोग आमतौर पर अन्य लोगों के उत्तरों में भारी बदलाव नहीं करने के पक्ष में हैं।
अवतरण

105

यदि आप सोच रहे हैं कि इस अनुकूलन को क्यों जोड़ा गया range.__contains__, और इसे 2.7 में क्यों नहीं जोड़ा गया xrange.__contains__:

सबसे पहले, जैसा कि अश्विनी चौधरी ने खोजा था , मुद्दा 1766304 को अनुकूलन के लिए स्पष्ट रूप से खोला गया था [x]range.__contains__। इसके लिए एक पैच को 3.2 के लिए स्वीकार किया गया और चेक किया गया , लेकिन 2.7 पर वापस नहीं भेजा गया क्योंकि "xrange ने इतने लंबे समय से इस तरह का व्यवहार किया है कि मैं यह नहीं देखता कि यह इस देर से पैच करने के लिए हमें क्या खरीदता है।" (2.7 उस समय लगभग बाहर था।)

इस दौरान:

मूल रूप से, xrangeएक नहीं-काफी-सीक्वेंस ऑब्जेक्ट था। के रूप में 3.1 डॉक्स कहते हैं:

रेंज ऑब्जेक्ट में बहुत कम व्यवहार होता है: वे केवल अनुक्रमण, पुनरावृत्ति और lenफ़ंक्शन का समर्थन करते हैं।

यह बिल्कुल सच नहीं था; एक xrangeवस्तु वास्तव में कुछ अन्य चीजें हैं जो अनुक्रमण और साथ स्वचालित रूप से आ समर्थित len, * सहित __contains__(रेखीय खोज के माध्यम से)। लेकिन किसी ने भी यह नहीं सोचा था कि यह उस समय उन्हें पूर्ण अनुक्रम बनाने के लायक था।

फिर, को लागू करने के भाग के रूप सार आधार वर्ग पीईपी, यह जो builtin प्रकार जो एबीसी को लागू करने के रूप में चिह्नित किया जाना चाहिए यह पता लगाने के लिए महत्वपूर्ण था, और xrange/ rangeलागू करने के लिए दावा किया collections.Sequenceहै, भले ही यह अभी भी केवल एक ही "बहुत कम व्यवहार" को संभाला। 9213 के अंक तक किसी ने भी उस समस्या पर ध्यान नहीं दिया । उस मुद्दे के लिए पैच न केवल जोड़ा गया indexऔर count3.2 के लिए range, यह भी अनुकूलित __contains__(जो एक ही गणित के साथ साझा करता है index, और सीधे द्वारा उपयोग किया जाता है count) फिर से काम किया । ** यह परिवर्तन 3.2 के रूप में अच्छी तरह से चला गया, और 2.x पर वापस नहीं भेजा गया, क्योंकि "यह एक बगफिक्स है जो नए तरीके जोड़ता है"। (इस बिंदु पर, 2.7 पहले से ही आरसी स्थिति था।)

इसलिए, इस अनुकूलन को 2.7 में वापस लाने के लिए दो मौके थे, लेकिन वे दोनों खारिज कर दिए गए थे।


* वास्तव में, आपको अकेले ही अनुक्रमण के साथ मुफ्त में पुनरावृति मिलती है, लेकिन 2.3 xrange वस्तुओं में एक कस्टम पुनरावृत्ति मिली।

** पहले संस्करण ने वास्तव में इसे लागू किया, और विवरण गलत मिला- जैसे, यह आपको देगा MyIntSubclass(2) in range(5) == False। लेकिन डैनियल स्टुट्ज़बैच के पैच के अपडेट किए गए संस्करण ने पिछले कोड के अधिकांश हिस्से को बहाल कर दिया, जिसमें जेनेरिक के लिए कमबैक भी शामिल था, धीमी गति _PySequence_IterSearchसे पूर्व-3.2 range.__contains__का उपयोग किया गया था जब अनुकूलन लागू नहीं होता था।


4
यहां टिप्पणियों से: सुधारxrange.__contains__ , ऐसा लगता है कि उन्होंने इसे उपयोगकर्ताओं के लिए आश्चर्य का एक तत्व छोड़ने के लिए केवल पायथन 2 के लिए वापस नहीं किया था और यह बहुत देर हो चुकी थी o_O। countऔर index पैच पर बाद में जोड़ा गया है। उस समय फाइल करें: hg.python.org/cpython/file/d599a3f2e72d/Objects/rangeobject.c
अश्विनी चौधरी

12
मैं एक भयावह शक है कि कुछ कोर अजगर devs अजगर 2.x के लिए करने के लिए "मुश्किल प्यार" आंशिक हैं क्योंकि वे दूर की बेहतर python3 :) करने के लिए स्विच करने के लिए लोगों को प्रोत्साहित करना चाहते हैं
विम

4
इसके अलावा मैं शर्त लगाता हूं कि पुराने संस्करणों में नई सुविधाओं को जोड़ना एक बहुत बड़ा बोझ है। कल्पना कीजिए अगर आप ओरेकल में गए और कहा, "देखो, मैं जावा 1.4 पर हूं और मैं लैंबडा एक्सप्रेशन के लायक हूं! उन्हें बिना किसी चीज के वापस लिए।"
रॉब

2
@RickTeachey हाँ, यह सिर्फ एक उदाहरण है। अगर मैंने कहा 1.7 यह अभी भी लागू होगा। यह एक मात्रात्मक अंतर है गुणात्मक नहीं है। मूल रूप से (अवैतनिक) देवता हमेशा के लिए 3.x में अच्छा नया सामान नहीं बना सकते हैं और इसे उन लोगों के लिए 2.x पर वापस भेज सकते हैं जो अपग्रेड नहीं करना चाहते हैं। यह एक बहुत बड़ा और हास्यास्पद बोझ है। क्या आपको लगता है कि मेरे तर्क में अभी भी कुछ गड़बड़ है?
रोब ग्रांट

3
@ रिकेटी: 2.7 3.1 से 3.2 के बीच थी, 3.3 के आसपास नहीं। और इसका मतलब है कि 2.7 आर सी में था जब 3.2 में अंतिम परिवर्तन हुए, जिससे बग टिप्पणियों को समझना आसान हो गया। वैसे भी, मुझे लगता है कि उन्होंने रेट्रोस्पेक्ट में कुछ गलतियाँ कीं (विशेष रूप से लोगों की मानें तो 2to3पुस्तकालयों की मदद से दोहरे-संस्करण कोड के बजाय इसके माध्यम से पलायन होगा six, यही कारण है कि हमें ऐसी चीजें मिलीं जैसे dict.viewkeysकिसी का कभी उपयोग नहीं करना है), और वहाँ थे कुछ बदलाव जो अभी 3.2 में बहुत देर से आए थे, लेकिन अधिकांश भाग के लिए 2.7 एक बहुत ही प्रभावशाली "पिछले 2.x कभी" रिलीज हुआ था।
abarnert

47

अन्य जवाबों ने इसे पहले से ही अच्छी तरह से समझाया, लेकिन मैं एक और प्रयोग की पेशकश करना चाहूंगा, जो रेंज ऑब्जेक्ट्स की प्रकृति को दिखाता है:

>>> r = range(5)
>>> for i in r:
        print(i, 2 in r, list(r))

0 True [0, 1, 2, 3, 4]
1 True [0, 1, 2, 3, 4]
2 True [0, 1, 2, 3, 4]
3 True [0, 1, 2, 3, 4]
4 True [0, 1, 2, 3, 4]

जैसा कि आप देख सकते हैं, एक श्रेणी वस्तु एक ऐसी वस्तु है जो अपनी सीमा को याद करती है और इसका उपयोग कई बार किया जा सकता है (यहां तक ​​कि इस पर पुनरावृत्ति करते हुए), न केवल एक बार का जनरेटर।


27

यह मूल्यांकन के लिए एक आलसी दृष्टिकोण और कुछ अतिरिक्त अनुकूलन के बारे में है range। वास्तविक उपयोग तक या अतिरिक्त अनुकूलन के कारण श्रेणियों में मानों की गणना करने की आवश्यकता नहीं है।

वैसे, आपका पूर्णांक इतना बड़ा नहीं है, विचार करें sys.maxsize

sys.maxsize in range(sys.maxsize) बहुत तेज है

अनुकूलन के कारण - दिए गए पूर्णांक को केवल न्यूनतम और अधिकतम सीमा के साथ तुलना करना आसान है।

परंतु:

Decimal(sys.maxsize) in range(sys.maxsize) बहुत धीमी है

(इस मामले में, कोई अनुकूलन नहीं है range, इसलिए यदि अजगर अप्रत्याशित दशमलव प्राप्त करता है, तो अजगर सभी नंबरों की तुलना करेगा)

आपको एक कार्यान्वयन विवरण के बारे में पता होना चाहिए, लेकिन उस पर भरोसा नहीं किया जाना चाहिए, क्योंकि यह भविष्य में बदल सकता है।


4
सावधान रहें बड़े पूर्णांक चल रहे हैं। अधिकांश मशीनों पर, float(sys.maxsize) != sys.maxsize)भले ही sys.maxsize-float(sys.maxsize) == 0
होल्डनवेब

18

टी एल; डॉ

द्वारा लौटाई गई वस्तु range()वास्तव में एक rangeवस्तु है। यह ऑब्जेक्ट पुनरावृत्ति इंटरफ़ेस को लागू करता है ताकि आप जनरेटर, सूची या टपल की तरह इसके मूल्यों पर क्रमिक रूप से पुनरावृति कर सकें।

लेकिन यह उस इंटरफ़ेस को भी लागू करता __contains__है जो वास्तव में क्या कहा जाता है जब inऑपरेटर के दाहिने हाथ पर कोई वस्तु दिखाई देती है । __contains__()एक विधि रिटर्न boolया नहीं, के बाएं हाथ की ओर मद की inवस्तु है। चूंकि rangeवस्तुओं को उनकी सीमा और स्ट्राइड पता है, इसलिए ओ (1) में इसे लागू करना बहुत आसान है।


0
  1. अनुकूलन के कारण, दिए गए पूर्णांकों की तुलना केवल न्यूनतम और अधिकतम सीमा के साथ करना बहुत आसान है।
  2. कारण यह है कि सीमा () समारोह python3 में इतनी तेजी से है कि यहाँ हम सीमा के लिए गणितीय तर्क का उपयोग करें, बल्कि सीमा वस्तु का एक सीधा यात्रा से है।
  3. तो यहाँ तर्क समझाने के लिए:
    • जाँच करें कि क्या संख्या प्रारंभ और रोक के बीच है।
    • जाँच करें कि क्या चरण परिशुद्धता मूल्य हमारी संख्या से अधिक नहीं है।
  4. एक उदाहरण लें, 997 सीमा में है (4, 1000, 3) क्योंकि:

    4 <= 997 < 1000, and (997 - 4) % 3 == 0.


1
क्या आप उसके लिए स्रोत साझा कर सकते हैं? यहां तक ​​कि अगर यह वैध लगता है, तो वास्तविक कोड द्वारा इन दावों को वापस करना अच्छा होगा
निको हसे

मुझे लगता है कि यह इसका एक उदाहरण है जिसे लागू किया जा सकता है। इसे लागू करने का सही तरीका नहीं है। हालांकि कोई संदर्भ नहीं दिया गया है, यह समझने के लिए अच्छा संकेत है कि रेंज के लिए समावेश की जाँच सूची या टपल से अधिक तेज़ क्यों हो सकती है
मोहम्मद शरीफ C

0

x-1 in (i for i in range(x))बड़े xमूल्यों के लिए प्रयास करें , जो range.__contains__अनुकूलन को लागू करने से बचने के लिए एक जनरेटर समझ का उपयोग करता है ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.