range ()보다 항상 xrange ()를 선호해야합니까?


460

그 이유는 무엇?


36
파이썬이 아닌 사람들을 위해 2의 차이점을 간단히 설명 할 수 있습니까? 어쩌면 "xrange ()와 같은 것이 allhing range ()를 수행하지만 X, Y 및 Z도 지원합니다"
Outlaw Programmer

87
range (n)은 모든 정수 0..n-1을 포함하는 목록을 만듭니다. range (1000000)를 수행하면> 4Mb 목록으로 끝나기 때문에 문제가됩니다. xrange는 목록 인 척하는 객체를 반환하여이 문제를 해결하지만 요청한 색인에서 필요한 수를 계산하여 반환합니다.
Brian


4
기본적으로, 반면에 range(1000)A는 list, xrange(1000)유사한 역할을하는 것을 목적으로한다 generator(이 분명하지만 없는 한). 또한 xrange빠릅니다. 당신이 할 수있는 import timeit from timeit한 후 바로이 방법 확인 for i in xrange: pass및 다른 range후 수행 timeit(method1)timeit(method2)와, 보라와 보라, xrange는 (당신이 목록을 필요로하지 않는 경우입니다) 빠른 속도로 때로는 거의 2 배이다. (나를 위해, 위해 i in xrange(1000):pass에 대한 대비는 i in range(1000):pass했다 13.31672596931457521.190124988555908각각 초 - 많이 있다고.)
dylnmc

또 다른 성능 테스트xrange(100)보다 20 % 더 빠릅니다 range(100).
Evgeni Sergeev 11:30에

답변:


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)

보시다시피, for 루프 나 이해에 사용되거나 이미 list ()로 래핑 된 경우 범위는 변경되지 않습니다.


5
"범위가 반복자가 될 것"은 무엇을 의미합니까? 이것이 "제너레이터"가 아니어야합니까?
Michael Mior

4
아니요. Generator는 특정 유형의 반복자를 지칭하며 새로운 range것은 반복자가 아닙니다.
user2357112는 Monica

두 번째 글 머리 기호는 실제로 의미가 없습니다. 객체를 여러 번 사용할 수 없다고 말하고 있습니다. 물론 넌 할 수있어! xr = xrange(1,11)다음 줄 for i in xr: print " ".join(format(i*j,"3d") for j in xr)과 짜잔 에서 시도 하십시오 ! 시간표는 최대 10 개입니다. 그것은 똑같이 작동 r = range(1,11)하며 for i in r: print " ".join(format(i*j,"3d") for j in r)... 모든 것이 Python2의 객체입니다. 나는 당신이 말한 range것과는 반대로 인덱스 기반의 이해력을 이해할 수 있다고 생각 합니다 xrange. 범위 내 생각은 매우 드물게 편리 없다
dylnmc

나는 (비록 충분하지 않은 공간) 것을 생각 range당신이 사용하려는 경우 유용 할 수 있습니다 list루프에서 다음이 목록에 특정 조건 또는 APPEND 물건에 따라 특정 인덱스를 변경 후 range확실히 낫다. 그러나 xrange단순히 더 빠르고 더 적은 메모리를 사용하므로 대부분의 for 루프 응용 프로그램에 가장 적합합니다. 질문자의 질문으로 돌아가는 사례는 거의 없지만 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보다 큰 숫자를 지원할 수 없다는 점입니다. 따라서 파이썬의 많은 수의 지원을 사용하여 범위를 원하면 range ()를 사용해야합니다.

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

range ()는 목록을 반환하고 xrange ()는 xrange 객체를 반환합니다.

xrange ()는 약간 빠르며 약간 더 메모리 효율적입니다. 그러나 이익은 그리 크지 않습니다.

리스트가 사용하는 여분의 메모리는 물론 낭비되지 않고리스트는 더 많은 기능 (슬라이스, 반복, 삽입 등)을 갖습니다. 정확한 차이점은 설명서 에서 찾을 수 있습니다 . 기본 규칙이 없으므로 필요한 것을 사용하십시오.

Python 3.0은 아직 개발 중이지만 IIRC range ()는 xrange () 2.X와 매우 유사하며 list (range ())를 사용하여 목록을 생성 할 수 있습니다.


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는이 모든 것을 어쨌든해야한다고 생각합니다 ...


네 동의했습니다. 완전히 다른 기술에서 lodash 작업을 확인하여 게으른 지 확인하십시오 : github.com/lodash/lodash/issues/274 . 슬라이싱 등은 가능한 한 게으르고 그렇지 않은 경우에만 수정하십시오.
Rob Grant

4

책에 나오는 좋은 예 : Practical Python By Magnus Lie Hetland

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

앞의 예제에서 xrange 대신 range를 사용하지 않는 것이 좋습니다. 처음 5 개의 숫자 만 필요하지만 range는 모든 숫자를 계산하므로 시간이 오래 걸릴 수 있습니다. xrange를 사용하면 필요한 숫자 만 계산하므로 문제가되지 않습니다.

네, @Brian의 답변을 읽었습니다. 파이썬 3에서 range ()는 생성기이며 xrange ()는 존재하지 않습니다.


3

다음과 같은 이유로 범위로 이동하십시오.

1) xrange는 최신 Python 버전에서 사라질 것입니다. 이를 통해 향후 호환성을 쉽게 얻을 수 있습니다.

2) 범위는 xrange와 관련된 효율성을 가져옵니다.


13
이러지 마 xrange ()는 사라지지만 다른 많은 것들도 사라질 것입니다. Python 2.x 코드를 Python 3.x 코드로 변환하는 데 사용할 도구는 xrange ()를 range ()로 자동 변환하지만 range ()는 덜 효율적인 목록 (range ())으로 변환됩니다.
Thomas Wouters

10
토마스 : 실제로는 그것보다 조금 더 똑똑합니다. 실제 목록 (예 : for 루프 또는 이해)이 필요없는 상황에서 range ()를 일반 range ()로 변환합니다. 변수에 할당되거나 직접 사용되는 경우에만 list ()로 감싸 야합니다.
Brian

2

자, 여기의 모든 사람들은 xrange와 range의 장단점에 대한 의견이 다릅니다. 그것들은 대부분 정확하고, xrange는 이터레이터이며, 범위를 정하고 실제 목록을 만듭니다. 대부분의 경우 실제로 두 가지의 차이점을 느끼지 못할 것입니다. (xrange가 아닌 range로 map을 사용할 수 있지만 더 많은 메모리를 사용합니다.)

그러나 당신이 집회를 듣고 싶다고 생각하는 것은 선호되는 선택이 xrange입니다. Python 3의 범위는 반복자이므로 코드 변환 도구 2to3은 xrange의 모든 사용을 범위로 올바르게 변환하고 범위 사용에 대한 오류 또는 경고를 발생시킵니다. 나중에 코드를 쉽게 변환하려면 xrange 만 사용하고 목록을 원할 때 list (xrange)를 사용합니다. 나는 시카고에서 PyCon (2008)에서 CPython 스프린트 동안 이것을 배웠다.


8
그건 사실이 아니야. "x in range (20)"와 같은 코드는 범위로 남고 "x = range (20)"와 같은 코드는 "x = list (range (20))"로 변환됩니다. 오류는 없습니다. 또한 2.6과 3.0에서 실행되는 코드를 작성하려면 호환성 기능을 추가하지 않고 range ()가 유일한 옵션입니다.
Brian

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에서 수행하는 데 사용 기간이없는 xrange()존재는 파이썬 3에서 range()같은 순서를 여러 번 반복하는 경우 실제로 일부 시나리오에서 더 빠르게 할 수 있습니다. xrange()매번 정수 객체를 재구성해야하지만 range()실제 정수 객체를 갖습니다.


2

하지만 xrange빠르게보다 range대부분의 경우, 성능 차이는 꽤 최소한이다. 아래의 작은 프로그램은 a range와 a에 대한 반복을 비교 합니다 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의 목록에 대해서만이 코드를 실행하고 있습니다.
Mark

실제로 목록 길이를 수정하는 것이 좋습니다.rtime = timeit.timeit('a=0;\nfor n in range(%d): a += n' % list_len, number=1000)
Mark

와, 고마워요. 고마워요. 고마워요. 여전히 큰 차이는 없습니다.
speedplane
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.