क्या आपको हमेशा रेंज () से अधिक xrange () का पक्ष लेना चाहिए?


460

क्यों या क्यों नहीं?


36
क्या कोई संक्षेप में हमारे लिए गैर-अजगर लोगों के लिए 2 के बीच के अंतर का वर्णन कर सकता है? हो सकता है कि "xrange () जैसे हर रेंज रेंज () करता है, लेकिन X, Y और Z को भी सपोर्ट करता है"
Outlaw Programmer

87
रेंज (एन) सभी पूर्णांक 0..n-1 युक्त एक सूची बनाता है। यदि आप रेंज (1000000) करते हैं तो यह एक समस्या है, क्योंकि आप एक> 4Mb सूची के साथ समाप्त हो जाएंगे। xrange एक ऐसी वस्तु को लौटाता है, जो एक सूची होने का दिखावा करती है, लेकिन सिर्फ उस सूचकांक से आवश्यक संख्या का पता लगाने के लिए काम करती है, और जो लौटाता है।
ब्रायन


4
मूल रूप से, जबकि range(1000), एक है list, xrange(1000)एक वस्तु है जो एक की तरह कार्य करता है generator(हालांकि यह निश्चित रूप से एक नहीं है)। इसके अलावा, xrangeतेज है। आप कर सकते हैं import timeit from timeitऔर फिर एक विधि है कि बस के लिए for i in xrange: passऔर एक और है range, तो करते हैं timeit(method1)और timeit(method2), लो और निहारना, xrange लगभग कभी कभी दो बार तेज है (कि जब आप एक सूची की जरूरत नहीं है)। (मेरे लिए, के लिए i in xrange(1000):passके लिए बनाम i in range(1000):passले लिया 13.316725969314575बनाम 21.190124988555908सेकंड क्रमशः - एक बहुत है।)
dylnmc

एक और प्रदर्शन परीक्षणxrange(100) 20% की तुलना में तेजी से देता है range(100)
इवगेनी सर्गेव

जवाबों:


443

प्रदर्शन के लिए, विशेष रूप से जब आप एक बड़ी सीमा पर पुनरावृत्ति कर रहे हों, xrange()आमतौर पर बेहतर होता है। हालाँकि, अभी भी कुछ मामले हैं जिन्हें आप पसंद कर सकते हैं range():

  • अजगर 3 में, range()वह करता है जो करता था xrange()और xrange()मौजूद नहीं था। यदि आप ऐसा कोड लिखना चाहते हैं जो Python 2 और Python 3 दोनों पर चलेगा, तो आप उपयोग नहीं कर सकते xrange()

  • range()वास्तव में कुछ मामलों में तेजी से हो सकता है - जैसे। यदि एक ही अनुक्रम में कई बार पुनरावृति होती है। xrange()हर बार पूर्णांक ऑब्जेक्ट को फिर से बनाना है, लेकिन range()वास्तविक पूर्णांक ऑब्जेक्ट होंगे। (यह हमेशा स्मृति के मामले में बदतर प्रदर्शन करेगा)

  • xrange()उन सभी मामलों में उपयोग करने योग्य नहीं है जहाँ एक वास्तविक सूची की आवश्यकता होती है। उदाहरण के लिए, यह स्लाइस या किसी सूची के तरीकों का समर्थन नहीं करता है।

[संपादित करें] range()2to3 टूल द्वारा अपग्रेड किए जाने का उल्लेख करने वाले कुछ पोस्ट हैं । रिकॉर्ड के लिए, यहाँ से कुछ नमूना प्रयोगों पर उपकरण चलाने का उत्पादन है range()औरxrange()

RefactoringTool: Skipping implicit fixer: buffer
RefactoringTool: Skipping implicit fixer: idioms
RefactoringTool: Skipping implicit fixer: ws_comma
--- range_test.py (original)
+++ range_test.py (refactored)
@@ -1,7 +1,7 @@

 for x in range(20):
-    a=range(20)
+    a=list(range(20))
     b=list(range(20))
     c=[x for x in range(20)]
     d=(x for x in range(20))
-    e=xrange(20)
+    e=range(20)

जैसा कि आप देख सकते हैं, जब लूप या कॉम्प्रिहेंशन के लिए उपयोग किया जाता है, या जहां पहले से सूची () के साथ लिपटे होते हैं, तो रेंज अपरिवर्तित रह जाती है।


5
"रेंज इट्रेटर बन जाएगा" से आपका क्या मतलब है? क्या यह "जनरेटर" नहीं होना चाहिए?
माइकल माइर

4
नहीं। जेनरेटर एक विशेष प्रकार के पुनरावृत्त को संदर्भित करता है, और नया rangeवैसे भी एक पुनरावृत्ति नहीं है।
user2357112

आपकी दूसरी गोली वास्तव में कोई मतलब नहीं रखती है। आप कह रहे हैं कि आप कई बार किसी वस्तु का उपयोग नहीं कर सकते हैं; जरूर आप कर सकते हो! xr = xrange(1,11)फिर अगली पंक्ति for i in xr: print " ".join(format(i*j,"3d") for j in xr)और वॉइला पर प्रयास करें ! आप अपने समय-सारणी दस तक हैं। यह बस के रूप में ही काम करता है r = range(1,11)और for i in r: print " ".join(format(i*j,"3d") for j in r)... सब कुछ Python2 में एक वस्तु है। मुझे लगता है कि आपके कहने का मतलब यह है कि आप इंडेक्स-बेस्ड कॉम्प्रिहेंशन (अगर समझ में आता है) कर सकते हैं तो बेहतर rangeहोगा xrange। रेंज बहुत ही कम है, मुझे लगता है
dylnmc

(पर्याप्त नहीं कमरा) हालांकि, मैं ऐसा लगता है कि rangeआप एक उपयोग करना चाहते हैं सुविधाजनक हो सकता है listएक पाश में और उसके बाद कुछ शर्तों या उस सूची को संलग्न चीजों के आधार पर कुछ सूचकांकों बदलने के लिए, तो rangeनिश्चित रूप से बेहतर है। हालांकि, xrangeबस तेज है और कम मेमोरी का उपयोग करता है, इसलिए लूप अनुप्रयोगों के लिए बहुमत के लिए, यह सबसे अच्छा प्रतीत होता है। ऐसे उदाहरण हैं - प्रश्नकर्ता के प्रश्न पर वापस जाना - शायद ही कभी, लेकिन जहाँ rangeबेहतर होगा। शायद उतनी शायद ही कभी न हो जितना मैं सोच रहा हूं, लेकिन मैं निश्चित रूप से xrange95% समय का उपयोग करता हूं ।
dylnmc

129

नहीं, वे दोनों उनके उपयोग हैं:

xrange()पुनरावृत्ति करते समय उपयोग करें , क्योंकि यह स्मृति को बचाता है। कहते हैं:

for x in xrange(1, one_zillion):

बजाय:

for x in range(1, one_zillion):

दूसरी ओर, उपयोग करें range()यदि आप वास्तव में संख्याओं की सूची चाहते हैं।

multiples_of_seven = range(7,100,7)
print "Multiples of seven < 100: ", multiples_of_seven

42

आप के पक्ष में होना चाहिए range()से अधिक xrange()केवल जब तुम एक वास्तविक सूची की जरूरत है। उदाहरण के लिए, जब आप सूची को संशोधित करना चाहते हैं range(), या जब आप इसे स्लाइस करना चाहते हैं। पुनरावृत्ति के लिए या यहां तक ​​कि सिर्फ सामान्य अनुक्रमण, xrange()ठीक काम करेगा (और आमतौर पर बहुत अधिक कुशलता से)। एक बिंदु है जहां बहुत छोटी सूचियों की range()तुलना में थोड़ा तेज़ है xrange(), लेकिन आपके हार्डवेयर और विभिन्न अन्य विवरणों के आधार पर, ब्रेक-ईवन लंबाई 1 या 2 के परिणामस्वरूप हो सकता है; चिंता की कोई बात नहीं। पसंद करते हैं xrange()


30

एक अन्य अंतर यह है कि xrange () C ints से बड़ी संख्या का समर्थन नहीं कर सकता है, इसलिए यदि आप बड़ी संख्या में समर्थन में अजगर के द्वारा निर्मित रेंज का उपयोग करना चाहते हैं, तो आपको रेंज () का उपयोग करना होगा।

Python 2.7.3 (default, Jul 13 2012, 22:29:01) 
[GCC 4.7.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> range(123456787676676767676676,123456787676676767676679)
[123456787676676767676676L, 123456787676676767676677L, 123456787676676767676678L]
>>> xrange(123456787676676767676676,123456787676676767676679)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: Python int too large to convert to C long

पायथन 3 में यह समस्या नहीं है:

Python 3.2.3 (default, Jul 14 2012, 01:01:48) 
[GCC 4.7.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> range(123456787676676767676676,123456787676676767676679)
range(123456787676676767676676, 123456787676676767676679)

13

xrange()अधिक कुशल है क्योंकि वस्तुओं की सूची बनाने के बजाय, यह एक समय में एक ही वस्तु उत्पन्न करता है। 100 पूर्णांक, और उनके सभी उपरि के बजाय, और उन्हें सूची में रखने के लिए, आपके पास एक समय में एक पूर्णांक होना चाहिए। तेज़ पीढ़ी, बेहतर मेमोरी उपयोग, अधिक कुशल कोड।

जब तक मुझे विशेष रूप से किसी चीज के लिए सूची की आवश्यकता नहीं होती है, मैं हमेशा एहसान करता हूं xrange()


8

श्रेणी () एक सूची देता है, xrange () एक xrange ऑब्जेक्ट देता है।

xrange () थोड़ा तेज़ है, और थोड़ा अधिक मेमोरी कुशल है। लेकिन लाभ बहुत बड़ा नहीं है।

किसी सूची द्वारा उपयोग की गई अतिरिक्त मेमोरी निश्चित रूप से व्यर्थ नहीं है, सूचियों में अधिक कार्यक्षमता (स्लाइस, दोहराना, सम्मिलित करना, ...) है। दस्तावेज़ में सटीक अंतर पाए जा सकते हैं । कोई हड्डी रोग नियम नहीं है, जो आवश्यक है उसका उपयोग करें।

पायथन 3.0 अभी भी विकास में है, लेकिन IIRC रेंज () 2.X के xrange () के समान होगी और सूचियों को उत्पन्न करने के लिए सूची (रेंज) का उपयोग किया जा सकता है।


5

मैं सिर्फ इतना कहना चाहूंगा कि यह वास्तव में टुकड़ा और अनुक्रमण कार्यक्षमता के साथ एक xrange ऑब्जेक्ट प्राप्त करना मुश्किल नहीं है। मैंने कुछ कोड लिखे हैं जो काफी खतरे का काम करते हैं और जब यह मायने रखता है (पुनरावृत्तियों) के लिए xrange जितना तेज़ है।

from __future__ import division

def read_xrange(xrange_object):
    # returns the xrange object's start, stop, and step
    start = xrange_object[0]
    if len(xrange_object) > 1:
       step = xrange_object[1] - xrange_object[0]
    else:
        step = 1
    stop = xrange_object[-1] + step
    return start, stop, step

class Xrange(object):
    ''' creates an xrange-like object that supports slicing and indexing.
    ex: a = Xrange(20)
    a.index(10)
    will work

    Also a[:5]
    will return another Xrange object with the specified attributes

    Also allows for the conversion from an existing xrange object
    '''
    def __init__(self, *inputs):
        # allow inputs of xrange objects
        if len(inputs) == 1:
            test, = inputs
            if type(test) == xrange:
                self.xrange = test
                self.start, self.stop, self.step = read_xrange(test)
                return

        # or create one from start, stop, step
        self.start, self.step = 0, None
        if len(inputs) == 1:
            self.stop, = inputs
        elif len(inputs) == 2:
            self.start, self.stop = inputs
        elif len(inputs) == 3:
            self.start, self.stop, self.step = inputs
        else:
            raise ValueError(inputs)

        self.xrange = xrange(self.start, self.stop, self.step)

    def __iter__(self):
        return iter(self.xrange)

    def __getitem__(self, item):
        if type(item) is int:
            if item < 0:
                item += len(self)

            return self.xrange[item]

        if type(item) is slice:
            # get the indexes, and then convert to the number
            start, stop, step = item.start, item.stop, item.step
            start = start if start != None else 0 # convert start = None to start = 0
            if start < 0:
                start += start
            start = self[start]
            if start < 0: raise IndexError(item)
            step = (self.step if self.step != None else 1) * (step if step != None else 1)
            stop = stop if stop is not None else self.xrange[-1]
            if stop < 0:
                stop += stop

            stop = self[stop]
            stop = stop

            if stop > self.stop:
                raise IndexError
            if start < self.start:
                raise IndexError
            return Xrange(start, stop, step)

    def index(self, value):
        error = ValueError('object.index({0}): {0} not in object'.format(value))
        index = (value - self.start)/self.step
        if index % 1 != 0:
            raise error
        index = int(index)


        try:
            self.xrange[index]
        except (IndexError, TypeError):
            raise error
        return index

    def __len__(self):
        return len(self.xrange)

ईमानदारी से, मुझे लगता है कि पूरा मुद्दा मूर्खतापूर्ण है और xrange को यह सब वैसे भी करना चाहिए ...


हाँ सहमत हुए; पूरी तरह से अलग तकनीक से, इसे आलसी बनाने के लिए लॉश पर काम की जाँच करें: github.com/lodash/lodash/issues/274 । स्लाइसिंग इत्यादि अभी भी यथासंभव आलसी होना चाहिए और जहां नहीं, केवल तभी पुनरावृत्ति करें।
रोब ग्रांट

4

पुस्तक में दिया गया एक अच्छा उदाहरण: प्रैक्टिकल पायथन बाय मैग्नस लाइ हेटलैंड

>>> zip(range(5), xrange(100000000))
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]

मैं पूर्ववर्ती उदाहरण में xrange के बजाय रेंज का उपयोग करने की अनुशंसा नहीं करूंगा- हालांकि केवल पहले पांच नंबर की आवश्यकता है, रेंज सभी संख्याओं की गणना करता है, और इसमें बहुत समय लग सकता है। Xrange के साथ, यह एक समस्या नहीं है क्योंकि यह केवल उन संख्याओं की गणना करता है जो आवश्यक हैं।

हां मैंने @ ब्रायन का उत्तर पढ़ा: अजगर 3 में, रेंज () वैसे भी एक जनरेटर है और xrange () मौजूद नहीं है।


3

इन कारणों के लिए सीमा के साथ जाएं:

1) नए पायथन संस्करणों में xrange दूर जा रहा है। इससे आपको भविष्य में अनुकूलता मिलती है।

2) रेंज xrange से जुड़ी क्षमता पर ले जाएगा।


13
यह मत करो। xrange () चले जाएंगे, लेकिन बहुत सारी अन्य चीजें होंगी। आप अपने पायथन 2.x कोड को पायथन 3.x कोड में अनुवाद करने के लिए जिस टूल का उपयोग कर रहे हैं, वह स्वचालित रूप से xrange () से लेकर रेंज () तक ट्रांसलेट होगा, लेकिन रेंज () का अनुवाद कम कुशल सूची (रेंज ()) में किया जाएगा।
थॉमस वाउचर

10
थॉमस: यह वास्तव में उससे थोड़ा अधिक चालाक है। यह उन स्थितियों में रेंज () का अनुवाद करेगा, जहां इसे स्पष्ट रूप से एक वास्तविक सूची की आवश्यकता नहीं है (जैसे कि लूप के लिए, या समझ के लिए) बस सादे रेंज () में। केवल ऐसे मामले जहां यह एक चर को सौंपा जाता है, या सीधे सूची के साथ लिपटे होना चाहिए ()
ब्रायन

2

ठीक है, यहाँ हर कोई एक अलग राय के रूप में व्यापार और xrange बनाम सीमा के फायदे के रूप में है। वे ज्यादातर सही हैं, xrange एक पुनरावृत्त है, और सीमा से बाहर है और एक वास्तविक सूची बनाता है। अधिकांश मामलों के लिए, आप वास्तव में दोनों के बीच अंतर नहीं देखेंगे। (आप रेंज के साथ मानचित्र का उपयोग कर सकते हैं, लेकिन xrange के साथ नहीं, बल्कि यह अधिक मेमोरी का उपयोग करता है।)

हालांकि, मुझे लगता है कि आप रैली सुनना चाहते हैं, हालांकि, पसंदीदा विकल्प xrange है। चूंकि पायथन 3 में रेंज एक इट्रेटर है, कोड रूपांतरण उपकरण 2to3 सही ढंग से रेंज के xrange के सभी उपयोगों को परिवर्तित करेगा, और रेंज के उपयोग के लिए एक त्रुटि या चेतावनी को बाहर कर देगा। यदि आप भविष्य में आसानी से अपने कोड को बदलना सुनिश्चित करना चाहते हैं, तो आप xrange का ही उपयोग करेंगे, और सूची (xrange) का जब आप सुनिश्चित करें कि आप एक सूची चाहते हैं। मैंने शिकागो में इस साल (2008) में पाइकॉन में सीपीथॉन स्प्रिंट के दौरान यह सीखा।


8
यह सच नहीं है। "एक्स फॉर रेंज (20)" की तरह कोड को सीमा के रूप में छोड़ दिया जाएगा, और "एक्स = रेंज (20)" जैसे कोड को "एक्स = सूची (रेंज (20))" में बदल दिया जाएगा - कोई त्रुटि नहीं। इसके अलावा, यदि आप ऐसा कोड लिखना चाहते हैं जो 2.6 और 3.0 दोनों के तहत चलेगा, तो रेंज () कॉम्पैटिबिलिटी फ़ंक्शंस को जोड़े बिना आपका एकमात्र विकल्प है।
ब्रायन

2
  • range(): range(1, 10)1 से 10 नंबरों की सूची देता है और पूरी सूची को मेमोरी में रखता है।
  • xrange(): जैसे range(), लेकिन एक सूची वापस करने के बजाय, एक ऐसी वस्तु लौटाता है जो मांग पर सीमा में संख्या उत्पन्न करती है। लूपिंग के लिए, यह हल्के से अधिक range()और अधिक कुशल मेमोरी है। xrange()एक पुनरावृत्ति की तरह वस्तु और मांग पर संख्या उत्पन्न करता है (आलसी मूल्यांकन)।
In [1]: range(1,10)
Out[1]: [1, 2, 3, 4, 5, 6, 7, 8, 9]

In [2]: xrange(10)
Out[2]: xrange(10)

In [3]: print xrange.__doc__
Out[3]: xrange([start,] stop[, step]) -> xrange object

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


2

जबकि xrangeतेजी से है rangeअधिकांश परिस्थितियों में, प्रदर्शन में अंतर बहुत कम है। नीचे दिए गए छोटे कार्यक्रम की तुलना में अधिक है rangeऔर एक xrange:

import timeit
# Try various list sizes.
for list_len in [1, 10, 100, 1000, 10000, 100000, 1000000]:
  # Time doing a range and an xrange.
  rtime = timeit.timeit('a=0;\nfor n in range(%d): a += n'%list_len, number=1000)
  xrtime = timeit.timeit('a=0;\nfor n in xrange(%d): a += n'%list_len, number=1000)
  # Print the result
  print "Loop list of len %d: range=%.4f, xrange=%.4f"%(list_len, rtime, xrtime)

नीचे दिए गए परिणाम से पता चलता है कि xrangeवास्तव में तेज है, लेकिन पसीना बहाने के लिए पर्याप्त नहीं है।

Loop list of len 1: range=0.0003, xrange=0.0003
Loop list of len 10: range=0.0013, xrange=0.0011
Loop list of len 100: range=0.0068, xrange=0.0034
Loop list of len 1000: range=0.0609, xrange=0.0438
Loop list of len 10000: range=0.5527, xrange=0.5266
Loop list of len 100000: range=10.1666, xrange=7.8481
Loop list of len 1000000: range=168.3425, xrange=155.8719

तो हर तरह से उपयोग करें xrange, लेकिन जब तक आप एक विवश हार्डवेयर पर नहीं होते हैं , तब तक इसके बारे में बहुत चिंता न करें।


आपका list_lenउपयोग नहीं किया जा रहा है और इसलिए आप इस कोड को केवल लंबाई 100 की सूची के लिए चला रहे हैं।
मार्क

मैं वास्तव में सूची की लंबाई को संशोधित करने की सलाह देता हूं:rtime = timeit.timeit('a=0;\nfor n in range(%d): a += n' % list_len, number=1000)
मार्क

वाह, यह एक अच्छा सप्ताह होने के लिए आकार दे रहा है, धन्यवाद, निश्चित। फिर भी, एक बड़ा अंतर नहीं है।
स्पीडप्लेन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.