Python 2.X에서 range와 xrange 함수의 차이점은 무엇입니까?


719

분명히 xrange는 빠르지 만 왜 그것이 더 빠르는지 (그리고 지금까지 일화 외에 더 빠르다는 증거가 없음) 또는 그 외에 다른 것이 무엇인지 전혀 모른다.

for i in range(0, 20):
for i in xrange(0, 20):

답변:


817

Python 2.x에서 :

  • range리스트를 생성하므로, 그렇게하면 range(1, 10000000)리스트를 메모리에리스트로 생성합니다 9999999.

  • xrange 느리게 평가되는 시퀀스 객체입니다.

파이썬 3에서는 rangepython 's와 동일 xrange하며 목록을 얻으려면을 사용해야 list(range(...))합니다.


65
xrange는 정확히 생성기는 아니지만 느리게 평가되고 생성기처럼 작동합니다.
Vaibhav Mishra

47
xrange(x).__iter__()발전기입니다.
augustomen

34
왜 그들은 범위를 게으르지 않고 xrange를 만들었습니까?
Rob Grant

22
@RobertGrant, 그들은했다. Python 3에서 (Python 2.x 라인에서는 그렇게 할 수 없었습니다. 모든 변경 사항은 이전 버전과 호환되어야하기 때문입니다.)
Paul Draper

12
@Ratul은 각각 i이 초기화가 아니라 요청시 평가됨을 의미합니다 .
Onilol

223

range는리스트를 생성하므로, 그렇게하면 range(1, 10000000)리스트를 메모리에리스트로 생성합니다 9999999.

xrange 는 생성기이므로 시퀀스 객체 느리게 평가됩니다.

이것은 사실이지만 Python 3에서는 .range()Python 2에 의해 구현됩니다 .xrange(). 실제로 목록을 생성해야하는 경우 다음을 수행해야합니다.

list(range(1,100))

3
그 범위가 같은 루프에 사용되는 인덱스를 생성하는 대부분이었다로 (기존 애플리케이션 파괴에 대한) 큰 문제가되고 표시되지 않습니다 "(10 일) 범위에서 난을 위해"
벤자민 Autin

10
+1이 답변 덕분에 범위를 xrange로 대체하는 Python 3에 대한 정보가 매우 유용합니다. 나는 실제로 누군가에게 xrange를 대신 사용하거나 범위를 사용하라고 말했고 그들은 파이썬 3에서는 중요하지 않다고 말했기 때문에 Google은 더 많은 정보를 검색
했으며이

xrange발전기 호출에 어떤 문제가 있습니까? yield명령문을 포함하는 함수 이며 용어집 에 따라 이러한 함수를 생성기라고합니다.
winterlight

@ winterlight, 그것에 대한 올바른 용어는 반복 자라고 생각하십시오. 발전기도받을 수 있어야합니다.
McSinyx

112

timeit모듈을 사용하여 작은 코드 스 니펫 중 어느 것이 더 빠른지 테스트 하십시오 !

$ python -m timeit 'for i in range(1000000):' ' pass'
10 loops, best of 3: 90.5 msec per loop
$ python -m timeit 'for i in xrange(1000000):' ' pass'
10 loops, best of 3: 51.1 msec per loop

개인적으로, 나는 정말로 거대한 목록 .range()을 다루지 않는 한 항상을 사용 합니다. 백만 항목 목록에 대해 시간별로 볼 수 있듯이 추가 오버 헤드는 0.04 초에 불과합니다. Corey가 지적했듯이 Python 3.0에서는 사라지고 어쨌든 멋진 반복기 동작을 제공합니다..xrange().range()


12
timeit 예제의 경우 +1 . 참고 : Windows에서 실행하는 즉 "그래서 코드가 될 것이다 따옴표를 사용하는 데 필요한 cmd를python -m timeit "for i in xrange(1000000):" " pass"
스토킹

10
xrange의 주요 이점은 시간이 아니라 메모리입니다.
endolith

3
실용 답변 +1 : 거대하지 않으면 범위를 사용하십시오 . BTW 그것들은 개념적으로 동일합니까? 이상하게도 답이 나오지 않습니다.
Bob Stein

6
xrange가 더 빠르고 메모리를 낭비하지 않는다면 왜 range를 사용합니까?
Austin Mohr

8
나는 귀하의 진술에 일반적으로 동의하지만 귀하의 평가는 잘못되었습니다 : the extra overhead is only 0.04 seconds그것을 보는 올바른 방법 (90.5-51.1)/51.1 = 1.771 times slower이 아닙니다. 이것이 프로그램의 핵심 루프 인 경우 잠재적으로 병목 현상을 일으킬 수 있음을 전달하기 때문에 정확합니다. 그러나 이것이 작은 부분이라면 1.77x는 그리 많지 않습니다.
chacham15

65

xrange범위 매개 변수 만 저장하고 요청시 숫자를 생성합니다. 그러나 Python의 C 구현은 현재 인수를 C long으로 제한합니다.

xrange(2**32-1, 2**32+1)  # When long is 32 bits, OverflowError: Python int too large to convert to C long
range(2**32-1, 2**32+1)   # OK --> [4294967295L, 4294967296L]

Python 3.0에는 range2.x xrange만 있고 최소 및 최대 끝점에 대한 제한이없는 것처럼 작동합니다 .


39

xrange는 반복자를 반환하고 한 번에 하나의 숫자 만 메모리에 유지합니다. range는 전체 숫자 목록을 메모리에 유지합니다.


9
xrange반복자를 반환 하지 않습니다 .
abarnert

and only keeps one number in memory at a time나머지는 어디로 안내해주세요 ..
SIslam

5
@SIslam 시작, 종료 및 현재를 알고 있으면 한 번에 하나씩 다음을 계산할 수 있습니다.
저스틴 메이 너

30

Library Reference 와 함께 시간을 보내십시오 . 익숙할수록 이와 같은 질문에 대한 답변을 더 빨리 찾을 수 있습니다. 내장 객체와 유형에 관한 처음 몇 장이 특히 중요합니다.

xrange 유형의 장점은 xrange 객체가 나타내는 범위의 크기에 관계없이 항상 동일한 양의 메모리를 사용한다는 것입니다. 일관된 성능 이점이 없습니다.

파이썬 구문에 대한 빠른 정보를 찾는 또 다른 방법은 docstring과 help-function입니다.

print xrange.__doc__ # def doc(x): print x.__doc__ is super useful
help(xrange)

1
도서관은 훌륭하지만 질문에 대한 답변을 얻는 것이 항상 쉬운 것은 아닙니다.
Teifion

2
라이브러리 참조로 이동하여 ctrl + f를 누르고 범위를 검색하면 두 가지 결과가 나타납니다. 이 질문에 대한 답을 찾기 위해 많은 노력을 기울이지 않습니다.
David Locke

1
라이브러리 참조가 작동하지 않습니다. 업데이트 하시겠습니까?
mk ..

14

나는 아무도 문서를 읽지 않는다는 충격을 받았다 .

이 함수는와 매우 유사 range()하지만 xrange목록 대신 객체를 반환 합니다. 이것은 실제로 모두 동시에 저장하지 않고 해당 목록과 동일한 값을 생성하는 불투명 시퀀스 유형입니다. 의 장점 xrange()이상은 range()최소한 (때문에 xrange()범위의 모든 요소는 루프가있는 경우로서 (사용되지 않는 경우 매우 큰 범위의 메모리가 부족한 시스템에서 사용 또는 경우를 제외하고 그들에게 물었을 때 여전히 가치를 창출 할 수있다) 일반적으로)로 끝납니다 break.


13

range는 목록을 생성하므로 range (1, 10000000)를 수행하면 10000000 개의 요소가있는 메모리에 목록이 생성됩니다. xrange는 생성기이므로 느리게 평가됩니다.

이것은 두 가지 장점을 제공합니다.

  1. 당신은 더 이상 목록을 반복하지 않고 얻을 수 있습니다 MemoryError .
  2. 각 숫자를 느리게 해결하므로 반복을 일찍 중지하면 전체 목록을 만드는 데 시간을 낭비하지 않습니다.

12

이 간단한 예제에서 xrangeover 의 장점을 찾을 수 있습니다 range.

import timeit

t1 = timeit.default_timer()
a = 0
for i in xrange(1, 100000000):
    pass
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 4.49153590202 seconds

t1 = timeit.default_timer()
a = 0
for i in range(1, 100000000):
    pass
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 7.04547905922 seconds

위의 예는 다음과 같은 경우에 실질적으로 더 나은 것을 반영하지 않습니다. xrange .

이제 다음과 range비교할 때 실제로 느린 경우를 살펴보십시오 xrange.

import timeit

t1 = timeit.default_timer()
a = 0
for i in xrange(1, 100000000):
    if i == 10000:
        break
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 0.000764846801758 seconds

t1 = timeit.default_timer()
a = 0
for i in range(1, 100000000):
    if i == 10000:
        break
t2 = timeit.default_timer() 

print "time taken: ", (t2-t1)  # 2.78506207466 seconds

을 사용 range하면 이미 0에서 100000000 (시간 소모)까지 목록을 만들지 xrange만 생성기이며 필요에 따라, 즉 반복이 계속되는 경우에만 숫자를 생성합니다.

Python-3에서 range기능 의 구현은 Python-2의 구현과 동일 하지만 Python-3에서는 xrange제거되었습니다 xrange.

행복한 코딩 !!


11

최적화상의 이유입니다.

range ()는 시작부터 끝까지 값 목록을 만듭니다 (예제에서는 0 .. 20). 이것은 매우 넓은 범위에서 비싼 작업이 될 것입니다.

반면에 xrange ()는 훨씬 더 최적화되었습니다. 필요한 경우 (xrange 시퀀스 객체를 통해) 다음 값만 계산하며 range ()와 같은 모든 값 목록을 만들지 않습니다.


9

range(x,y)for루프 를 사용하면 x와 y 사이의 각 숫자 목록을 반환하고 range느립니다. 실제로 range인덱스 범위가 더 큽니다. range(x.y)x와 y 사이의 모든 숫자 목록을 인쇄합니다.

xrange(x,y)반환 xrange(x,y)하지만 for루프 를 사용하면 xrange더 빠릅니다. xrange색인 범위가 더 작습니다. xrange인쇄 할 xrange(x,y)뿐만 아니라 안에있는 모든 숫자를 계속 유지합니다.

[In] range(1,10)
[Out] [1, 2, 3, 4, 5, 6, 7, 8, 9]
[In] xrange(1,10)
[Out] xrange(1,10)

for루프 를 사용하면 작동합니다.

[In] for i in range(1,10):
        print i
[Out] 1
      2
      3
      4
      5
      6
      7
      8
      9
[In] for i in xrange(1,10):
         print i
[Out] 1
      2
      3
      4
      5
      6
      7
      8
      9

루프를 사용할 때 큰 차이는 없지만 인쇄 만하면 차이가 있습니다!


8

range () : range (1, 10)은 1에서 10까지의 숫자로 된 목록을 반환하고 전체 목록을 메모리에 담습니다.

xrange () : range ()와 비슷하지만 목록을 반환하는 대신 요청시 범위의 숫자를 생성하는 객체를 반환합니다. 루핑의 경우 이것은 range ()보다 약간 빠르며 메모리 효율이 높습니다. 반복자와 같은 xrange () 객체는 필요에 따라 숫자를 생성합니다. (Lazy Evaluation)

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__

xrange([start,] stop[, step]) -> xrange object

6

다른 답변 중 일부는 파이썬 3 2.X의를 제거 언급 range하고 2.X의 개명 xrangerange. 그러나 3.0 또는 3.1을 사용하지 않는 한 (아무도 없어야 함) 실제로는 약간 다른 유형입니다.

3.1 문서에서 말한 것처럼 :

범위 객체는 동작이 거의 없습니다. 인덱싱, 반복 및 len함수 만 지원합니다 .

그러나 3.2+에서는 range전체 시퀀스입니다. 확장 슬라이스 및와 collections.abc.Sequence동일한 의미를 갖는 모든 방법을 지원 합니다 list. *

그리고 적어도 CPython 및 PyPy (현재 존재하는 유일한 두 개의 3.2 이상 구현)에서는 indexand count메소드와 in연산자 (정수만 전달하는 한)의 상수 시간 구현 도 있습니다. 이것은 쓰기 123456 in r가 3.2+에서 합리적이고 2.7 또는 3.1에서는 끔찍한 아이디어라는 것을 의미합니다.


* 2.6-2.7 및 3.0-3.1에서 issubclass(xrange, collections.Sequence)반환 되는 사실 은 3.2에서 수정되었고 백 포트되지 않은 버그 입니다.True


6

파이썬 2.x에서

range (x) 는 x 요소를 갖는 메모리에서 작성된 목록을 리턴합니다.

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

xrange (x) 는 필요에 따라 숫자를 생성하는 생성기 obj 인 xrange 객체를 반환합니다. 그들은 for-loop (Lazy Evaluation) 동안 계산됩니다.

루핑의 경우 이것은 range ()보다 약간 빠르며 메모리 효율이 높습니다.

>>> b = xrange(5)
>>> b
xrange(5)

xrange()발전기가 아닙니다. xrange(n).__ iter __ ()`입니다.
th3an0maly

5

루프에서 xrange에 대해 범위를 테스트 할 때 ( timeit을 사용해야한다는 것을 알고 있지만 간단한 목록 이해 예제를 사용하여 메모리에서 신속하게 해킹되었습니다) 다음을 발견했습니다.

import time

for x in range(1, 10):

    t = time.time()
    [v*10 for v in range(1, 10000)]
    print "range:  %.4f" % ((time.time()-t)*100)

    t = time.time()
    [v*10 for v in xrange(1, 10000)]
    print "xrange: %.4f" % ((time.time()-t)*100)

이것은 다음을 제공합니다.

$python range_tests.py
range:  0.4273
xrange: 0.3733
range:  0.3881
xrange: 0.3507
range:  0.3712
xrange: 0.3565
range:  0.4031
xrange: 0.3558
range:  0.3714
xrange: 0.3520
range:  0.3834
xrange: 0.3546
range:  0.3717
xrange: 0.3511
range:  0.3745
xrange: 0.3523
range:  0.3858
xrange: 0.3997 <- garbage collection?

또는 for 루프에서 xrange를 사용하십시오.

range:  0.4172
xrange: 0.3701
range:  0.3840
xrange: 0.3547
range:  0.3830
xrange: 0.3862 <- garbage collection?
range:  0.4019
xrange: 0.3532
range:  0.3738
xrange: 0.3726
range:  0.3762
xrange: 0.3533
range:  0.3710
xrange: 0.3509
range:  0.3738
xrange: 0.3512
range:  0.3703
xrange: 0.3509

내 스 니펫 테스트가 제대로됩니까? xrange의 느린 인스턴스에 대한 의견이 있습니까? 또는 더 나은 예 :-)


2
이와 같은 벤치 마크를 한 번 실행하면 정확한 타이밍 결과가 제공되지 않습니다. 항상 차이가 있습니다. GC 또는 CPU를 훔치는 다른 프로세스가 될 수 있습니다. 벤치 마크는 일반적 10-100-1000 -... 실행되는 이유
Vajk Hermecz

이것은 단지 서두르는 스 니펫 인쇄물입니다. 필자는 몇 번만 실행했지만 최대 약 100 개였으며 xrangePython 3에서는 비교가 중복되었지만 약간 더 빨랐습니다.
Dave Everitt

3
이것은 무엇 timeit을위한 것입니다. 여러 번 실행하고 GC를 비활성화하고 대신 가장 좋은 시계를 사용하는 time등 의 작업 을 처리합니다.
abarnert

4

파이썬에서 xrange ()와 range ()는 user와 비슷하게 작동하지만 두 함수를 사용하여 메모리가 어떻게 할당되는지에 대해 이야기 할 때 차이점이 있습니다.

range ()를 사용할 때 생성하는 모든 변수에 메모리를 할당하므로 더 큰 no와 함께 사용하지 않는 것이 좋습니다. 생성 될 변수

반면에 xrange ()는 한 번에 특정 값만 생성하며 필요한 모든 값을 인쇄하기 위해 for 루프와 함께 만 사용할 수 있습니다.


3

range는 전체 목록을 생성하고 반환합니다. xrange는 그렇지 않습니다-요청시 목록에 숫자를 생성합니다.


2

xrange는 반복자를 사용하고 (즉시 값을 생성) range는 목록을 반환합니다.


2

뭐?
range런타임시 정적 목록을 반환합니다. 필요할 때마다 값이 생성되는 (생성기는 아니지만 생성기처럼 작동)를
xrange반환 object합니다.

언제 사용합니까?

  • 사용하다 xrange특히 휴대폰과 같은 "메모리 감지 시스템"이있는 경우 10 억과 같은 거대한 범위의 목록을 생성하려는 경우에 .
  • range목록을 여러 번 반복 하려면 사용하십시오 .

추신 : Python 3.x의 range기능 == Python 2.x의 xrange기능.


xrange생성기 객체를 반환하지 않습니다.
abarnert

내가 올바르게 이해한다면, 그것이 여기에 설명되어 있습니다 (Python 2.x의 경우) : wiki.python.org/moin/Generators
kmario23

그렇다면 위키가 잘못되었습니다. (나는 "SH"가 누가 그 의견을 추가하고 서명했는지 모른다.) 공식 문서 는 옳다; 직접 테스트하여 생성기인지 시퀀스인지 확인할 수 있습니다.
abarnert

확인. 그러나 이것을 읽은 후에도 여전히 혼란 스럽습니다 : stackoverflow.com/questions/135041/…
kmario23

1
재미있는 질문은 통역사가 공식 문서 나 다른 통역자와 동의하지 않을 때해야 할 일입니다. 그러나 다행히도, 그렇게 자주 나오지 않습니다…
abarnert

2

모두가 그것을 크게 설명했습니다. 그러나 나는 그것을 직접보고 싶었습니다. python3을 사용합니다. 그래서 Windows에서 리소스 모니터를 열고 먼저 다음 명령을 먼저 실행했습니다.

a=0
for i in range(1,100000):
    a=a+i

그런 다음 '사용 중'메모리의 변경 사항을 확인했습니다. 중요하지 않았습니다. 그런 다음 다음 코드를 실행했습니다.

for i in list(range(1,100000)):
    a=a+i

그리고 즉시 사용하기 위해 많은 양의 메모리가 필요했습니다. 그리고 나는 확신했다. 직접 시도해 볼 수 있습니다.

Python 2X를 사용하는 경우 첫 번째 코드에서 'range ()'를 'xrange ()'로 바꾸고 'list (range ())'를 'range ()'로 바꿉니다.


2

도움 문서에서.

파이썬 2.7.12

>>> print range.__doc__
range(stop) -> list of integers
range(start, stop[, step]) -> list of integers

Return a list containing an arithmetic progression of integers.
range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
When step is given, it specifies the increment (or decrement).
For example, range(4) returns [0, 1, 2, 3].  The end point is omitted!
These are exactly the valid indices for a list of 4 elements.

>>> print xrange.__doc__
xrange(stop) -> xrange object
xrange(start, stop[, step]) -> xrange object

Like range(), but instead of returning a list, returns an object that
generates the numbers in the range on demand.  For looping, this is 
slightly faster than range() and more memory efficient.

파이썬 3.5.2

>>> print(range.__doc__)
range(stop) -> range object
range(start, stop[, step]) -> range object

Return an object that produces a sequence of integers from start (inclusive)
to stop (exclusive) by step.  range(i, j) produces i, i+1, i+2, ..., j-1.
start defaults to 0, and stop is omitted!  range(4) produces 0, 1, 2, 3.
These are exactly the valid indices for a list of 4 elements.
When step is given, it specifies the increment (or decrement).

>>> print(xrange.__doc__)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'xrange' is not defined

차이가 분명하다. Python 2.x에서는 range목록을 xrange반환하고 반복 가능한 xrange 객체를 반환합니다.

파이썬 3.x의에서 range이된다 xrange파이썬 2.x에서의, 그리고 xrange제거됩니다.


1

0-N 항목의 스캔 / 인쇄 요구 사항에서 range 및 xrange는 다음과 같이 작동합니다.

range ()-메모리에 새 목록을 만들고 0에서 N까지의 전체 항목 (전체적으로 N + 1)을 가져 와서 인쇄합니다. xrange ()-항목을 스캔하고 현재 발견 된 항목 만 메모리에 유지하는 반복자 인스턴스를 작성하므로 항상 동일한 양의 메모리를 사용합니다.

필요한 요소가 목록의 시작 부분에있는 경우 상당한 시간과 메모리를 절약합니다.


1
xrange반복자 인스턴스를 작성하지 않습니다. 그것은 생성 xrange반복 가능하다 객체를하지만, 목록 좋아하지 반복자-거의 (그러나 확실히) 시퀀스.
abarnert

1

범위목록 을 반환 하지만 xrange 는 범위 크기에 관계없이 동일한 메모리를 사용 하는 xrange 객체를 반환합니다. 메모리에서 사용할 수 있습니다.


1

작은 인수가 range(..)/에 대한 차이가 줄어 듭니다 xrange(..).

$ python -m timeit "for i in xrange(10111):" " for k in range(100):" "  pass"
10 loops, best of 3: 59.4 msec per loop

$ python -m timeit "for i in xrange(10111):" " for k in xrange(100):" "  pass"
10 loops, best of 3: 46.9 msec per loop

이 경우 xrange(100)약 20 % 더 효율적입니다.


1

range : -range는 모든 것을 한 번에 채우므로 모든 범위의 숫자가 메모리를 차지합니다.

xrange :-xrange는 발전기와 같은 것입니다. 숫자의 범위를 원할 때 그림으로 들어 오지만 for loop.so 메모리에서 효율적으로 사용하려는 경우와 같이 저장하지 않으려 고합니다.


1

또한 do list(xrange(...))가에 해당합니다 range(...).

너무 list느립니다.

또한 xrange시퀀스를 완전히 끝내지 않습니다.

그렇기 때문에 목록이 아닌 xrange개체입니다.


1

range() 파이썬에서 2.x

이 함수는 본질적으로 range()Python에서 사용할 수 있었던 이전 함수이며 지정된 범위의 요소를 포함 2.x하는 list객체 의 인스턴스를 반환합니다 .

그러나이 구현은 숫자 범위가있는 목록을 초기화 할 때 너무 비효율적입니다. 예를 들어, for i in range(1000000)메모리에이 목록을 저장해야하므로 메모리 및 시간 사용량 측면에서 실행하는 데 비용이 많이 드는 명령입니다.


range()파이썬 3.xxrange()파이썬2.x

파이썬 3.x은 새로운 구현을 도입했습니다 range()(새로운 구현은 이미 Python을 2.x통해 사용할 수 있었지만xrange() 함수를 ).

range()공격은 게으른 평가 라는 전략을 이용합니다 . 대신 범위에있는 요소의 거대한 목록을 만드는, 새로운 구현 클래스 소개 range, 지정된 범위에서 필요한 요소를 나타내는 경량 객체를 메모리에 명시 적으로 저장하지 않고 (이 발전기처럼 들릴 수도 있지만 게으른 평가의 개념은 다른).


예를 들어 다음을 고려하십시오.

# Python 2.x
>>> a = range(10)
>>> type(a)
<type 'list'>
>>> b = xrange(10)
>>> type(b)
<type 'xrange'>

# Python 3.x
>>> a = range(10)
>>> type(a)
<class 'range'>

-2

게시물 보기범위와 xrange의 차이점을 찾으려면 을 .

인용 :

range0으로 시작하는 정의 된 길이의 연속 정수 목록을 정확하게 반환합니다 xrange. 그러나 "xrange object"를 반환합니다 .이 반복자는 반복자와 같은 역할을합니다.


2
나는 이것이 5 살이라는 것을 알고 있지만 그 게시물은 거의 모든 것에 대해 잘못되었습니다. xrange반복자가 아닙니다. 에 의해 반환 된 range목록은 반복을 지원합니다 (목록은 반복 가능한 프로토 타입 예제입니다). 전체적인 이점은 xrange"최소한"것이 아닙니다. 등등.
abarnert
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.