2012-07-13 5 views
127

가능한 중복 :
Python “is” operator behaves unexpectedly with integers왜 (0-6)은 -6 = 거짓입니까?

오늘은 내 프로젝트를 디버깅하기 위해 노력하고 분석하는 몇 시간 후에 나는이 도착했던 :

>>> (0-6) is -6 
False 

하지만,

>>> (0-5) is -5 
True 

나에게 설명해 주시겠습니까? 그 이유는 무엇입니까? 이것은 일종의 버그 또는 매우 이상한 행동 일 수 있습니다. -5에서 포함 256

> Python 2.7.3 (default, Apr 24 2012, 00:00:54) [GCC 4.7.0 20120414 (prerelease)] on linux2 
>>> type(0-6) 
<type 'int'> 
>>> type(-6) 
<type 'int'> 
>>> type((0-6) is -6) 
<type 'bool'> 
>>> 
+6

그게 완전히 엉뚱한 – Wug

+25

무엇이 당신을 처음에 'is'라고 사용하게 했습니까? 'is/is not None'을 제외하고 파이썬에서 자주 사용되어야하는 것은 아닙니다. –

+3

@ Russel의 코멘트는 머리에 못을 박는 다. 문제는 누군가가 분명히 숫자를 비교하기 위해 "is"를 사용하고 있으며 그것이 잘못된 기대 인'='처럼 기능 할 것으로 예상한다는 것이다. – LarsH

답변

150

모든 정수 따라서 is 테스트를 통과, CPython과 함께 같은 주소를 공유 전역 개체로 캐시됩니다.

이 아티팩트는 http://www.laurentluce.com/posts/python-integer-objects-implementation/으로 자세하게 설명되어 있으며 현재 소스 코드는 http://hg.python.org/cpython/file/tip/Objects/longobject.c입니다.

특정 구조는 작은 정수를 참조하고 공유하므로 액세스가 빠릅니다. 정수 객체에 대한 262 개의 포인터 배열입니다. 정수 객체는 위에서 보았던 정수 객체 블록에서 초기화 중에 할당됩니다. 작은 정수 범위는 -5부터 257까지입니다. 대부분의 Python 프로그램은 해당 범위의 정수를 사용하여 많은 시간을 소비하므로 현명한 결정입니다.

이것은 CPython의 구현 세부 사항이므로 여기에 의존해서는 안됩니다. 예를 들어, PyPyid 정수를 구현하여 자신을 반환하므로 (0-6) is -6은 내부적으로 "다른 객체"인 경우에도 항상 true입니다. 또한이 정수 캐싱을 사용할지 여부를 구성하고 상한 및 하한도 설정할 수 있습니다. 그러나 일반적으로 다른 출처에서 검색된 객체는 동일하지 않습니다. 평등을 비교하려면 ==을 사용하십시오.

+2

그것이 내가 말하려고했던 것이지만, 올바르게 말로 표현할 수는 없습니다. +1 – mpen

+1

긍정적 인 측면에 흥미로운 스큐. 이 기사에서는'많은 파이썬 프로그램이 그 범위에서 정수를 사용하는데 많은 시간을 소비한다. '라고 말하며, 개발자들은 아마도 그것을 어떻게 든 측정했을 것입니다. 음수 리터럴은 요즘 오류 코드에만 사용되는 것 같아요. –

+0

감사합니다. @KennyTM. –

26

이것은 버그가 아닙니다. is은 동등성 테스트가 아닙니다. ==은 예상 된 결과를 제공합니다.

이 동작의 기술적 인 이유는 Python 구현이 동일한 상수 값의 다른 인스턴스를 동일한 객체 또는 다른 객체로 처리 할 수 ​​있다는 것입니다. 사용하고있는 파이썬 구현은 메모리 절약을 위해 특정 작은 정수를 동일한 객체를 공유하도록 선택합니다. 이 동작이 버전과 동일한 버전이나 다른 Python 구현에 의존 할 수는 없습니다.

+2

>'is'는 평등 테스트가 아닙니다. 이. 'is'는 두 개의 객체가 똑같은지 확인하기위한 신원 확인 테스트입니다. CPython 구현에서 일부 int 객체가 캐싱되는 것입니다. – Darthfett

+1

+1 파이썬 개발자가 없다면, 나에게 가장 좋은 설명이다. –

29

파이썬은 인터프리터에서 -5-256 범위의 정수를 저장합니다. 파이썬은 이러한 정수가 반환되는 정수 객체 풀을 가지고 있습니다. 그렇기 때문에 그 물체가 같은 이유입니다 : (0-5)-5이 아니라 그 자리에서 생성되는 (0-6)-6이 아닙니다.

여기 CPython과의 소스 코드의 소스입니다 :

#define NSMALLPOSINTS   257 
#define NSMALLNEGINTS   5 
static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS]; 

(view CPython source code : /trunk/Objects/intobject.c).소스 코드는 다음과 주석이 포함

/* References to small integers are saved in this array so that they 
    can be shared. 
    The integers that are saved are those in the range 
    -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive). 
*/ 

is 연산자는 다음 그들 (-5)를 비교합니다 동등한를 그들에있을 것 같은 개체 (같은 메모리 위치)하지만 다른 두 개의 새로운 정수 (-6) 때문에 다른 메모리 위치 (그리고 isTrue을 반환하지 않습니다). 위 소스 코드에서 257은 양의 정수이므로 0 - 256 (포함)입니다.

(source)

16

CPython의 일부 작은 정수 작은 문자열을 캐시하고 그 객체 동일한 id()의 모든 인스턴스를 제공하기 때문에이 일어나고있다.

(0-5)-5 문자열에 대한 유사 0-6-6

>>> id((0-6)) 
12064324 
>>> id((-6)) 
12064276 
>>> id((0-5)) 
10022392 
>>> id((-5)) 
10022392 

에 대한 사실이 아니다 id()에 대한 동일한 값을 가지고 : 문자열 캐싱에 대한 자세한 내용은

>>> x = 'abc' 
>>> y = 'abc' 
>>> x is y 
True 
>>> x = 'a little big string' 
>>> y = 'a little big string' 
>>> x is y 
False 

를 읽기 : is operator behaves differently when comparing strings with spaces

+2

그렇다면 왜'-6'이 'big'과 '-5' 아닌가요? "큰"옷을 입기위한 자격 기준은 무엇입니까? – inspectorG4dget

+1

CPython의 경우 -5-256은 "interned"(캐시 됨)입니다. 다소 임의적 인 구현 선택입니다. 주어진 interned 객체가 많이 사용되면 잠재적으로 큰 메모리 절감 효과가 있지만 런타임이나 메모리에서 interning하는 데 비용이 들기 때문에 모든 작업을 수행하고 싶지는 않습니다. –

+0

+1은 ID를 보여줍니다. 나는 그 것을 나의 대답에 추가하려하고 있었다. –