Python 3에서 왜 "1000000000000000 in range (1000000000000001)"가 그렇게 빠릅니까?


2115

range()실제로 파이썬 3의 객체 유형 인 함수 는 생성기와 비슷한 내용을 즉시 생성 한다는 것을 이해합니다 .

이 경우 다음 줄이 시간이 많이 걸릴 것이라고 예상했을 것입니다.

1000000000000000 in range(1000000000000001)

또한 : 내가 추가하는 0의 수에 관계없이 계산에는 거의 동일한 시간 (기본적으로 순간)이 소요됩니다.

나는 또한 이와 같은 것을 시도했지만 계산은 거의 즉각적입니다.

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()후드 아래 에서 물체가 너무 빨라지는 것은 무엇입니까?


Martijn Pieters의 답변 은 완전성으로 선정되었지만 Python 3에서 본격적인 시퀀스 가되는 의미 와 Python 구현 에서 함수 최적화 에 대한 잠재적 불일치에 관한 정보 / 경고에 대한 좋은 토론을위한 abarnert의 첫 번째 답변 을 참조하십시오 . abarnert의 다른 대답 은 좀 더 자세하게 설명하고 Python 3의 최적화 뒤에 숨겨진 역사에 관심이있는 사람들을위한 링크를 제공합니다 ( Python 2 의 최적화 부족 ). pokewim의 답변 은 관심있는 사람들을위한 관련 C 소스 코드와 설명을 제공합니다.range__contains__xrange


70
이것은 우리가 확인하는 항목이 bool또는 long유형 인 경우에만 해당 되며 다른 객체 유형에서는 미쳐 버립니다. 로 시도 :100000000000000.0 in range(1000000000000001)
애쉬 위니 Chaudhary에게

10
누가 range발전기 라고 말 했어요 ?
abarnert

7
@ abarnert 나는 내가 만든 편집이 혼란을 남겼다고 생각합니다.
Rick은 Monica를

5
@AshwiniChaudhary는 Python2가 Python3 xrange과 동일range 하지 않습니까 ?
Superbest

28
@Superbest xrange()객체에는 __contains__메소드 가 없으므로 항목 검사가 모든 항목을 반복해야합니다. 플러스 몇 가지 다른 변화가있다 range()는 슬라이스를 지원 (다시 돌려처럼, range이 또한 지금 개체) 및 countindex방법과 호환되도록 collections.SequenceABC.
Ashwini Chaudhary

답변:


2170

Python 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)을 제공하면 포함 된 모든 값 목록에 대해 포함 테스트를 사용하는 것처럼 일치하는 항목이 있는지 확인하기 위해 느린 스캔이 시작됩니다. 이것은 정수로 등식 테스트를 지원하기 위해 발생하지만 정수 산술도 지원하지 않는 다른 숫자 유형을 계속 지원하기 위해 수행되었습니다. 포함 테스트를 구현 한 원래 Python 문제 를 참조하십시오 .


* 파이썬 정수는 제한이 없기 때문에 거의 일정한 시간에 N이 증가함에 따라 수학 연산도 시간이 지남에 따라 O (log N) 연산이됩니다. 모두 최적화 된 C 코드에서 실행되고 Python은 정수 값을 30 비트 청크로 저장하므로 여기에 관련된 정수의 크기로 인해 성능에 영향을 미치기 전에 메모리가 부족합니다.


58
재미있는 사실 : 당신의 작업 구현을 가지고 있기 때문에 __getitem____len____iter__구현이 실제로 필요하지 않습니다.
Lucretiel

2
@Lucretiel : Python 2.3에서는xrangeiterator 빠르지 않기 때문에 특별하게 추가되었습니다. 그런 다음 3.x 어딘가에 (3.0 또는 3.2인지 확실하지 않음) 던져졌으며 사용하는 것과 동일한 listiterator유형을 list사용합니다.
abarnert

1
생성자를로 정의하고 def __init__(self, *start_stop_step)거기에서 구문 분석합니다. 이제 인수에 라벨이 붙어있는 방식은 혼란 스럽습니다. 그럼에도 불구하고 +1; 당신은 여전히 ​​그 행동을 분명히 설명했습니다.
Cody Piersall

1
@CodyPiersall : 불행히도, 그것은 실제 클래스의 이니셜 라이저의 서명입니다. range이전 버전 *args( argclinicC-API 함수가 완전한 Python 서명을 가질 수있게 하는 API) 다른 몇 가지 오래된 기능 (그리고 몇 가지 새로운 기능은 같은 xrange, slice그리고 itertools.islice, 일관성) 작업 같은 방식으로하지만, 대부분의 경우, 귀도와 핵심 개발자들의 나머지는 당신과 동의하는 것처럼 보인다. 2.0+ 문서 range는 실제 혼란스러운 서명을 표시하지 않고 C ++ 스타일의 오버로드 인 것처럼 설명 하고 친구 에게도 설명 합니다.
abarnert

2
@CodyPiersall : 실제로, argclinicNick Coghlan이 range명확하게 정의 할 수있는 방법을 생각해 냈을 때 Guido의 토론 에서 인용 한 것이 있습니다. 그래서 나는 그것이 range서면으로 혼동 된다는 데 동의합니다 .
abarnert

844

여기에서 근본적인 오해는 그것이 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

차이 rangeA는 listA가 있다는 것이다 rangeA는 지연 또는 동적 서열; 그것의 모든 값을 기억하지 않고, 단지 그것의 기억 start, stop그리고 step및에 필요에 따라 값을 생성합니다 __getitem__.

(부수적으로 print(iter(a)), 당신 range과 같은 listiterator유형 을 사용하는 것을 알 수 list있습니다. 어떻게 작동합니까? A listiteratorlistC 구현을 제공한다는 사실을 제외하고는 특별한 것을 사용하지 않으므로 __getitem__잘 작동합니다. range너무.)


이제는 Sequence.__contains__시간이 일정 해야한다는 말 list은 없습니다. 실제로와 같은 시퀀스의 명백한 예 는 그렇지 않습니다. 그러나 말할 수없는 것은 없습니다. 그리고 실제로 모든 값을 생성하고 테스트하는 것보다 range.__contains__수학적으로 검사하는 것이 더 쉽습니다 ( (val - start) % step그러나 부정적인 단계를 처리하는 데 약간의 복잡성이 있음). 왜 더 나은 방법을 사용 해서는 안 됩니까?

그러나이 일이 일어날 것을 보장 하는 언어에는 아무것도없는 것 같습니다 . Ashwini Chaudhari가 지적했듯이 정수로 변환하고 수학 테스트를 수행하는 대신 정수가 아닌 값을 지정하면 모든 값을 반복하고 하나씩 비교하는 것으로 넘어갑니다. 그리고 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시퀀스 유형으로) .
Rick은 Monica를

4
@ RickTeachey : 실제로 2.6 이상 (나는 2.5 이상) xrange도 시퀀스입니다. 2.7 문서를 참조하십시오 . 사실, 그것은 항상 거의 연속적이었습니다.
abarnert

5
@ RickTeachey : 사실, 나는 틀렸다; 2.6-2.7 (및 3.0-3.1)에서는 시퀀스 라고 주장 하지만 여전히 거의 순서입니다. 내 다른 대답을 참조하십시오.
abarnert

2
그것은 반복자가 아니며, 시퀀스 (자바에서는 반복 가능, C #에서는 IEnumerable)입니다- .__iter__()반복자를 반환하는 메소드가있는 것입니다. 그것은 차례로 한 번만 사용할 수 있습니다.
Johnth

4
@ThomasAhle : range정수가 아닌 경우 유형을 확인하지 않기 때문에 유형 __eq__에 호환되는 유형이 항상 가능하기 때문에 가능합니다 int. 물론 str작동하지 않지만 분명히 거기에있을 수없는 모든 유형을 명시 적으로 확인하여 작업 속도를 늦추고 싶지는 않았습니다 (결국 str하위 클래스는 재정의 __eq__하고에 포함될 수 있음 range).
ShadowRanger

377

소스를 사용하십시오 , Luke!

CPython에서 range(...).__contains__(메소드 래퍼)는 결국 값이 범위 내에있을 수 있는지 확인하는 간단한 계산에 위임합니다. 여기서 속도의 이유 는 range object의 직접 반복이 아닌 경계에 대한 수학적 추론을 사용 하기 때문 입니다. 사용 된 논리를 설명하려면 :

  1. 숫자 사이에 있는지 확인 start하고 stop, 그리고
  2. 보폭이 우리의 숫자를 "스텝"하지 않는지 확인하십시오.

예를 들어, 994range(4, 1000, 2)이유는

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

전체 C 코드가 아래에 포함되어 있는데, 메모리 관리 및 참조 횟수 세부 사항으로 인해 좀 더 장황하지만 기본 아이디어는 다음과 같습니다.

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

Martijn의 답변에 추가하기 위해 이것은 소스 의 관련 부분입니다 (C에서 범위 객체가 원시 코드로 작성 됨).

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객체 ( intPython 3에 있음)의 경우 range_contains_long함수를 사용하여 결과를 결정합니다. 그리고이 함수는 본질적으로 ob지정된 범위에 있는지 확인합니다 (C에서는 조금 더 복잡해 보이지만).

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
@ChrisWesseling : Martijn의 대답을 편집하는 것이 여기에 적합하지 않았을 정도로 이것은 충분히 다른 정보라고 생각합니다. 판결 요청이지만 사람들은 일반적으로 다른 사람들의 답변에 급격한 변화를 일으키지 않는 측면에서 잘못을 저지 릅니다.
abarnert

105

이 최적화가 추가 range.__contains__되었고 왜 2.7에서 추가 되지 않은지 궁금하다면 xrange.__contains__:

먼저 Ashwini Chaudhary가 발견 한대로 1766304 호를 명시 적으로 열어 최적화했습니다 [x]range.__contains__. 이것에 대한 패치는 3.2에 대해 승인되고 체크인 되었지만 "xrange는 오랫동안 이와 같이 동작하여 늦게 패치를 커밋하기 위해 구입 한 것이 무엇인지 알 수 없기 때문에"2.7로 백 포트되지 않았습니다. (2.7이 시점에서 거의 나갔다.)

그 동안에:

원래, xrange비 순차적 객체였습니다. 3.1 문서에서 말한 것처럼 :

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

이것은 사실이 아니었다. xrange객체는 실제로 인덱싱 및 자동 와서 몇 가지 다른 일을 지원 len, * 를 포함하여 __contains__(선형 검색을 통해). 그러나 당시에는 전체 시퀀스를 만들 가치가 있다고 생각한 사람은 아무도 없었습니다.

그런 다음, 구현의 일환으로 추상 기본 클래스 PEP를, 내장 유형이있는 상식을 구현으로 표시되어야하는 파악하는 것이 중요합니다, 그리고 xrange/ range구현하는 주장 collections.Sequence이 여전히 동일한 "아주 작은 행동을"처리에도 불구하고. 9213 호 까지는 그 문제를 눈치 채지 못했습니다 . 그 문제에 대한 패치뿐만 아니라 추가 index하고 count3.2의에 range, 그것은 또한 최적화 된 가공 된 재 __contains__(에있는 주 같은 수학을 index직접 데 사용됩니다 count). ** 이 변경 은 3.2에도 적용되었으며 "새로운 방법을 추가하는 버그 수정"이기 때문에 2.x로 백 포트되지 않았습니다. (이 시점에서 2.7은 이미 rc 상태를 지났습니다.)

따라서이 최적화를 2.7로 백 포트 할 수있는 두 가지 기회가 있었지만 둘 다 거부되었습니다.


실제로 인덱싱만으로도 무료로 반복을 얻을 수 있지만 2.3 xrange 개체에는 사용자 정의 반복자가 있습니다.

** 첫 번째 버전은 실제로 그것을 다시 구현하고 세부 사항이 잘못되었습니다. 예를 들어 그것은 당신에게 줄 것 MyIntSubclass(2) in range(5) == False입니다. 그러나 Daniel Stutzbach의 업데이트 된 패치 버전은 일반에 대한 폴백을 포함하여 이전 코드의 대부분을 복원 하여 최적화가 적용되지 않을 때 _PySequence_IterSearch3.2 이전 버전 range.__contains__이 암시 적으로 사용 하는 속도 를 늦췄습니다 .


4
여기에 의견 : 개선xrange.__contains__ , 그것은 사용자에게 놀라움의 요소를 남기기 위해 파이썬 2로 백 포트하지 않은 것처럼 보이며 너무 늦었습니다. countindex 패치는 나중에 추가되었습니다. : 그 시간에 파일 hg.python.org/cpython/file/d599a3f2e72d/Objects/rangeobject.c
애쉬 위니 Chaudhary

12
나는 일부 핵심 파이썬 개발자들이 사람들이 먼 곳에있는 python3으로 전환하도록 장려하기 때문에 python 2.x에 대한 "터프한 사랑"에 부분적으로 존재한다는 의심을 가지고있다 :)
wim

4
또한 이전 버전에 새로운 기능을 추가 해야하는 것은 큰 부담입니다. 오라클에 가서 "자바 1.4를 사용하고 있고 람다식이 필요합니다. 아무 것도 지원하지 않습니다."라고 말했습니다.
Rob Grant

2
@RickTeachey 네, 이것은 단지 예일뿐입니다. 1.7이라고 말하면 여전히 적용됩니다. 질적이지 않은 양적 차이입니다. 기본적으로 (무급) 개발자는 3.x에서 멋진 새로운 것을 영원히 만들 수 없으며 업그레이드하지 않으려는 사람들을 위해 2.x로 백 포트 할 수 없습니다. 엄청나게 말도 안되는 부담입니다. 내 추론에 여전히 문제가 있다고 생각합니까?
Rob Grant

3
@RickTeachey : 2.7은 3.3이 아닌 3.1과 3.2 사이였습니다. 마지막으로 3.2로 변경했을 때 2.7이 rc에 있었기 때문에 버그 주석을 더 쉽게 이해할 수 있습니다. 어쨌든, 그들은 회고에서 몇 가지 실수를했다고 생각합니다 (특히 사람들이 2to3와 같은 라이브러리의 도움으로 이중 버전 코드 대신에 마이그레이션한다고 가정하기 six때문에 dict.viewkeys아무도 사용하지 않는 것과 같은 것을 얻었습니다 ). 3.2에서 너무 늦게 나온 몇 가지 변경 사항은 대부분 2.7에서 상당히 인상적인 "last 2.x ever"릴리스였습니다.
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파이썬이 예기치 않은 Decimal을 받으면 파이썬은 모든 숫자를 비교합니다)

구현 세부 사항을 알고 있어야하지만 나중에 변경 될 수 있으므로 의존해서는 안됩니다.


4
큰 정수를 부동으로주의하십시오. float(sys.maxsize) != sys.maxsize)그래도 대부분의 컴퓨터에서 sys.maxsize-float(sys.maxsize) == 0.
holdenweb

18

TL; DR

에 의해 반환 된 객체 range()는 실제로range 객체입니다. 이 객체는 반복자 인터페이스를 구현하므로 생성기, 목록 또는 튜플과 같이 값을 순차적으로 반복 할 수 있습니다.

그러나 객체가 연산자 의 오른쪽에 나타날 때 실제로 호출되는 인터페이스 구현합니다 . 방법 복귀 의 왼쪽 사이드에 있는지의 여부는 항목의 목적이다. 객체는 그들의 한계와 걸음을 알고 있으므로 O (1)에서 구현하기가 매우 쉽습니다.__contains__in__contains__()boolinrange


0
  1. 최적화로 인해 주어진 정수를 최소 및 최대 범위와 비교하는 것이 매우 쉽습니다.
  2. 파이썬 (3) 에서 range () 함수가 너무 빠르기 때문에 여기서는 범위 객체를 직접 반복하는 것이 아니라 범위에 대해 수학적인 추론을 사용하기 때문입니다.
  3. 여기에 논리를 설명하십시오.
    • 숫자가 시작과 중지 사이에 있는지 확인하십시오.
    • 스텝 정밀도 값이 숫자를 넘지 않는지 확인하십시오.
  4. 예를 들어, 997의 범위는 (4, 1000, 3) 입니다.

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


1
그것에 대한 소스를 공유 할 수 있습니까? 그 소리가 합법적하더라도, 그것은 실제 코드가 이러한 주장을 백업 할 좋은 것
니코 하세

나는 이것이 구현 될 수있는 예라고 생각합니다. 그것이 구현되는 정확한 방법은 아닙니다. 참조가 제공되지 않았지만 범위에 대한 포함 확인이 목록 또는 튜플보다 훨씬 빠른 이유를 이해하기에 충분한 힌트가 충분합니다.
Mohammed Shareef C

0

최적화 를 호출하지 않도록 생성기 이해를 사용하는 x-1 in (i for i in range(x))x값을 시도하십시오 range.__contains__.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.