ㅍㅍㅋㄷ

python range() 와 xrange() 차이 본문

Programming/Python

python range() 와 xrange() 차이

클쏭 2016. 7. 15. 12:01

python range() 와 xrange() 차이    * python 2.X 기준 입니다.



해당 포스팅은 python 2를 기반으로 한 내용이다.

python 3 에서는 range() 와 xrange() 가 통합되어 range() 만 제공되며, 그 특성은 xrange() 와 동일하다. 



range()


 먼저, python docs 에서 range() 함수에 대한 설명을 보자. 


range(start, stop[, step])


  This is a versatile function to create lists containing arithmetic progressions. It is most often used in for loops. The arguments must be plain integers. If the step argument is omitted, it defaults to 1. If the start argument is omitted, it defaults to 0. The full form returns a list of plain integers [start, start + step, start + 2 * step, ...]. If step is positive, the last element is the largest start + i * step less than stop; if step is negative, the last element is the smallest start + i * step greater than stop. step must not be zero (or else ValueError is raised). Example:


 range() 함수는 built-in 함수로 일정 간격의 정수 값을 가진 list 를 생성해주는 함수이다. 함수의 argument 는 총 3개로 적어도 1개의 argument 를 지정 해 주어야 한다. 


  range(start, stop, step)

     - start : list 시작

     - stop : list 끝

     - step : 증가값


  만약 argument 중 하나만 지정하면 자동으로 stop 값을 설정하게 되며, 나머지 start 와 step 은 자동으로 0, 1로 지정된다. argument를 두개 지정하면 자동으로 start 와 stop 값을 설정 하게 되며 증가값은 1로 지정된다. 

 

>>> range(10)

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> range(0,10)

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> range(0, 10, 1)

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]


range(10) 과 range(0, 10) 과 range(0, 10, 1) 은 동일한 결과이다. 


>>> range(1, 10, 3)

[1, 4, 7]

>>> range(2, 20, 3)

[2, 5, 8, 11, 14, 17]


start 로 지정한 정수 값에서부터 시작하며, stop 으로 지정한 정수 값이 넘지 않는 범위에서 list 가 종료된다. 


>>> range(20, 10, -1)

[20, 19, 18, 17, 16, 15, 14, 13, 12, 11]

>>> range(20, 10, -2)

[20, 18, 16, 14, 12]


증가값을 마이너스로 하면, 위와 같이 정수 list 를 내림차순으로 저장할 수도 있다.  


이러한 range() 함수가 가장 유용하게 쓰이는 경우는 아마도 for 문과 함께 사용할때 일 것이다.


>>> for x in range(5):

...     print x

...

0

1

2

3

4




xrange() 와 xrange type


xrange(start, stop[, step])


  This function is very similar to range(), but returns an xrange object instead of a list. This is an opaque sequence type which yields the same values as the corresponding list, without actually storing them all simultaneously. The advantage of xrange() over range() is minimal (since xrange() still has to create the values when asked for them) except when a very large range is used on a memory-starved machine or when all of the range’s elements are never used (such as when the loop is usually terminated with break). For more information on xrange objects, see XRange Type and Sequence Types — str, unicode, list, tuple, bytearray, buffer, xrange.


 xrange() 함수는 range() 함수와 매우 비슷하다. for 문을 사용하면 아래와 같이 생성된 정수에 순차적으로 접근이 가능한 것을 볼 수 있다. 이는 range() 함수와 다르지 않다.  


>>> for x in xrange(5):

...     print x

...

0

1

2

3

4


그렇다면, xrange() 함수와 range() 는 다른점이 무엇일까. 

가장 큰 차이는 함수 결과 값의 데이터 type 이다.

range() 로 생성된 결과값의 타입은 list 이지만, xrange() 로 생성된 결과의 타입은 xrange 이다.


>>> type(range(10))

<type 'list'>

>>> type(xrange(10))

<type 'xrange'>


xrange 타입의 특징을 한번 살펴보자. 


xrange type


  The xrange type is an immutable sequence which is commonly used for looping. The advantage of the xrange type is that an xrange object will always take the same amount of memory, no matter the size of the range it represents. There are no consistent performance advantages.


XRange objects have very little behavior: they only support indexing, iteration, and the len() function.


 xrange 타입은 수정이 불가한 순차적 접근 가능한 데이터 타입이다. xrange 타입의 장점이라고 하면 지정한 데이터 크기에 상관없이 memory 할당량이 일정하다는 것이다. 


>>> import sys

>>> sys.getsizeof(range(10))

152

>>> sys.getsizeof(range(10000))

80072

>>> sys.getsizeof(xrange(10))

40

sys.getsizeof(xrange(10000))

40


위와 같이 range() 의 경우 list의 사이즈가 클 수록 그만큼의 메모리 사용량이 늘어난다. 하지만, xrange() 의 경우에는 할당량이 커져도 차지하는 메모리 사이즈는 동일하다. 따라서 지정하는 범위가 커지면 커질 수록 range() 보다 xrange() 를 사용했을 때의 메모리 사용 효율이 커지게 된다. 


어떻게 이게 가능할까?


 보통 range()를 사용하면 그 결과 값의 타입은 list 이다. list 타입의 경우에는 list 안에 속한 모든 데이터를 메모리에 적재하게 된다. 따라서 list의 크기 만큼 차지하는 메모리가 늘어나는 구조이다.


 하지만, xrange() 를 실행할 경우는 동작 방식이 조금 다르다. 자신에 속한 데이터값을 한꺼번에 메모리에 로드 하는 것이 아니라 해당 값에 접근 할때마다 그 값을 하나씩 로딩하는 방식이다. 그 구조가 마치 generator의 yield 를 사용했을때와 동일한 효과라고 생각하면 된다. 


 하지만 이러한 구조적 차이 때문에 xrange type의 경우는 list 에서 사용하는 다양한 함수를 사용하지 못한다. 모든 값을 한번에 메모리에 적재해서 사용하지 않기 때문에, list에서 즐겨 사용하던 slice 나 operand 작업도 불가능하다. 


>>> xrange(10)[:3]

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

TypeError: sequence index must be integer, not 'slice'


>>> xrange(5) + xrange(5)

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

TypeError: unsupported operand type(s) for +: 'xrange' and 'xrange'


 하지만, for 문과 같이 순차적 접근(iteration) 이나 index를 이용한 접근 위주의 용도로 사용할 경우에는 xrange() 를 사용하는게 훨씬 메모리 효율적이다. 따라서 그 용도를 잘 파악한다면, 훨씬 효율적인 코딩이 될 것이다. 


 


    




[ 참고 ]


  • https://docs.python.org/2/library/functions.html#range
  • https://docs.python.org/2/library/functions.html#xrange
  • https://docs.python.org/2/library/stdtypes.html#typesseq-xrange


Comments