"is"연산자는 정수로 예기치 않게 동작합니다


509

왜 파이썬에서 다음이 예기치 않게 동작합니까?

>>> a = 256
>>> b = 256
>>> a is b
True           # This is an expected result
>>> a = 257
>>> b = 257
>>> a is b
False          # What happened here? Why is this False?
>>> 257 is 257
True           # Yet the literal numbers compare properly

Python 2.5.2를 사용하고 있습니다. 다른 버전의 Python을 시도해 보면 Python 2.3.3은 99와 100 사이의 위의 동작을 보여줍니다.

위의 내용을 바탕으로 파이썬이 내부적으로 구현되어 "작은"정수가 큰 정수와 다른 방식으로 저장되고 is연산자가 그 차이를 알 수 있다고 가정 할 수 있습니다. 왜 새는 추상화인가? 두 개의 임의의 객체를 비교하여 숫자인지 여부를 미리 알 수없는 경우 동일한 지 여부를 확인하는 더 좋은 방법은 무엇입니까?


1
여기 > 현재 구현은 모든 정수 객체의 배열을 유지> 실제로 단지 기존 개체에 대한 참조를 다시 얻을> 사이에 -5 256, 당신은 그 범위 당신의 int를 만들 정수.
user5319825

2
이것은 CPython 특정 구현 세부 사항이며 정의되지 않은 동작입니다.주의해서 사용하십시오
ospider

답변:


392

이것 좀 봐 :

>>> a = 256
>>> b = 256
>>> id(a)
9987148
>>> id(b)
9987148
>>> a = 257
>>> b = 257
>>> id(a)
11662816
>>> id(b)
11662828

다음은 Python 2 문서 "Plain Integer Objects" 에서 찾은 내용입니다 ( Python 3 과 동일 ).

현재 구현에서는 -5와 256 사이의 모든 정수에 대해 정수 객체 배열을 유지합니다.이 범위에서 int를 만들면 실제로는 기존 객체에 대한 참조를 다시 얻습니다. 따라서 1의 값을 변경할 수 있어야합니다.이 경우 Python의 동작이 정의되지 않은 것 같습니다. :-)


46
그 범위 (-5, 256)가 어떻게 선택되었는지 아는 사람이 있습니까? 나는 그것이 (0, 255) 또는 심지어 (-255, 255)라면 너무 놀라지 않을 것이지만 -5에서 시작하는 262 숫자의 범위는 놀랍게도 임의적 인 것처럼 보입니다.
Woodrow Barlow

6
@WoodrowBarlow : -5는 일반적인 부정적인 자리 표시자를 캡처하는 휴리스틱 일뿐입니다. 0..255는 단일 바이트 값의 배열을 포함합니다. 그것은 256의 신비이지만, 정수를 바이트로 (바이트에서) 어셈블하기위한 것 같습니다.
Davis Herring

3
내가 이해하는 것에서 범위는 여러 프로젝트 (및 여러 언어)에서 일반적으로 사용되는 값을보고 선택되었습니다.
Tony Suffolk 66

9
reddit.com/r/Python/comments/18leav/… 에 따르면 범위는 [-5,100]이었습니다. 전체 범위의 바이트 값에 256을 더하여 256을 더하여 확장되었습니다. 왜냐하면 아마도 일반적인 숫자이기 때문입니다.
mwfearnley

2
@Ashwani는 2 년 전에 게시 된 댓글 바로 옆에있는 댓글을 읽으면 질문에 대한 답변을 찾을 수 있습니다.
jbg

116

파이썬의 "is"연산자는 정수로 예기치 않게 동작합니까?

요약하자면 다음과 같이 강조하겠습니다 . 정수를 비교 하는 is데 사용하지 마십시오 .

이것은 당신이 기대해야 할 행동이 아닙니다.

대신, 사용 ==!=각각 평등과 불평등에 대한 비교. 예를 들면 다음과 같습니다.

>>> a = 1000
>>> a == 1000       # Test integers like this,
True
>>> a != 5000       # or this!
True
>>> a is 1000       # Don't do this! - Don't use `is` to test integers!!
False

설명

이것을 알기 위해서는 다음을 알아야합니다.

첫째, 무엇을 is합니까? 비교 연산자입니다. 로부터 문서 :

x와 y가 동일한 객체 인 경우에만 연산자 isis not객체 동일성 테스트 : x is ytrue입니다. x is not y역 진리 값을 산출합니다.

따라서 다음은 동일합니다.

>>> a is b
>>> id(a) == id(b)

로부터 문서 :

id 객체의 "정체성"을 반환합니다. 이것은 수명 동안이 개체에 대해 고유하고 일정하게 보장되는 정수 (또는 긴 정수)입니다. 겹치지 않는 수명을 가진 두 개체의 id()값 이 동일 할 수 있습니다 .

CPython에서 객체의 id (Python의 참조 구현)가 메모리의 위치라는 사실은 구현 세부 사항입니다. 다른 Python 구현 (예 : Jython 또는 IronPython)은에 대해 다른 구현을 쉽게 가질 수 있습니다 id.

그렇다면 유스 케이스는 is무엇입니까? PEP8은 다음을 설명합니다 .

싱글 톤과의 비교 None는 항상 등호 연산자를 사용하지 않고 is또는 로 수행해야합니다 is not.

질문

다음 질문을 묻고 진술하십시오 (코드 포함).

왜 파이썬에서 다음이 예기치 않게 동작합니까?

>>> a = 256
>>> b = 256
>>> a is b
True           # This is an expected result

예상 된 결과 가 아닙니다 . 왜 예상됩니까? 그것은 단지 가치 정수는 것을 의미 256모두에 의해 참조 ab정수의 동일한 인스턴스입니다. 정수는 파이썬에서 변경할 수 없으므로 변경할 수 없습니다. 이것은 코드에 영향을 미치지 않습니다. 예상해서는 안됩니다. 그것은 단지 구현 세부 사항입니다.

그러나 우리는 256이라는 값을 말할 때마다 메모리에 새로운 별도의 인스턴스가 없다는 것을 기뻐해야 할 것입니다.

>>> a = 257
>>> b = 257
>>> a is b
False          # What happened here? Why is this False?

257메모리에 값이 있는 두 개의 정수 인스턴스가있는 것처럼 보입니다 . 정수는 불변이므로 메모리가 낭비됩니다. 우리가 많이 낭비하지 않기를 바랍니다. 우리는 아마 아닐 것입니다. 그러나이 동작은 보장되지 않습니다.

>>> 257 is 257
True           # Yet the literal numbers compare properly

글쎄, 이것은 파이썬의 특정 구현이 똑똑하고 노력하지 않는 한 메모리에 중복 값을 생성하지 않는 것처럼 보입니다. CPython 인 Python의 참조 구현을 사용하고 있음을 나타냅니다. CPython에 좋습니다.

CPython이 전 세계적 으로이 작업을 수행 할 수 있다면 더 저렴 할 수 있다면 (검색 비용이 들기 때문에) 아마도 다른 구현 일 수 있습니다.

그러나 코드에 미치는 영향에 대해서는 정수가 정수의 특정 인스턴스인지는 신경 쓰지 않아야합니다. 해당 인스턴스의 값이 무엇인지 신경 써야하며, 이에 대한 일반 비교 연산자를 사용해야합니다 (예 :) ==.

무엇 is합니까

is검사는 것을 id두 객체는 동일하다. CPython에서이 id위치는 메모리의 위치이지만 다른 구현에서 다른 고유 번호 일 수 있습니다. 이것을 코드로 바꾸려면 :

>>> a is b

와 같다

>>> id(a) == id(b)

왜 우리가 사용하고 싶 is습니까?

이것은 두 개의 매우 긴 문자열의 값이 같은지 확인하는 것과 비교하여 매우 빠른 검사 일 수 있습니다. 그러나 객체의 고유성에 적용되므로 사용 사례가 제한적입니다. 실제로 우리는 주로 None싱글 톤 (메모리의 한 곳에 존재하는 유일한 인스턴스) 인 확인에 사용하려고합니다 . 서로 혼동 할 가능성이있는 경우 다른 싱글 톤을 만들 수도 있습니다 is. 다음은 예제입니다 (Python 2 및 3에서 작동). 예 :

SENTINEL_SINGLETON = object() # this will only be created one time.

def foo(keyword_argument=None):
    if keyword_argument is None:
        print('no argument given to foo')
    bar()
    bar(keyword_argument)
    bar('baz')

def bar(keyword_argument=SENTINEL_SINGLETON):
    # SENTINEL_SINGLETON tells us if we were not passed anything
    # as None is a legitimate potential argument we could get.
    if keyword_argument is SENTINEL_SINGLETON:
        print('no argument given to bar')
    else:
        print('argument to bar: {0}'.format(keyword_argument))

foo()

어떤 지문 :

no argument given to foo
no argument given to bar
argument to bar: None
argument to bar: baz

따라서 is센티넬을 사용하면 bar인수없이 호출 할 때 와 호출 할 때를 구분할 수 None있습니다. 이것들은 주요 유스 케이스입니다 is- 정수, 문자열, 튜플 또는 이와 같은 다른 것들의 동등성을 테스트하는 데 사용 하지 마십시오 .


"이것은 주요 유스 케이스 is입니다. 정수, 문자열, 튜플 또는 이와 유사한 것들이 같은지 테스트하는 데 사용하지 마십시오." 그러나 간단한 상태 시스템을 클래스에 통합하려고하는데 상태는 관찰 가능한 속성 만 같거나 다른 불투명 한 값이기 때문에와 비교할 수있는 것은 자연 스럽습니다 is. 내부 문자열을 상태로 사용하려고합니다. 나는 일반 정수를 선호하지만 불행히도 파이썬은 정수를 인턴 할 수 없습니다 ( 0 is 0구현 세부 사항입니다).
Alexey

@Alexey는 열거 형이 필요한 것처럼 들립니까? stackoverflow.com/questions/37601644/…
Aaron Hall

아마, 고마워, 그들은 몰랐다. 이것은 당신이 IMO에 대답하는 적절한 추가 일 수 있습니다.
Alexey

아마도 대답에 센티넬과 같은 많은 바보 같은 물체를 사용하는 것이 더 가벼운 해결책 일 것입니다.
Alexey

@Alexey 열거 형은 Python 3 표준 라이브러리에 있으며 코드가 맨손 센티넬보다 조금 더 의미가 있습니다.
Aaron Hall

60

두 가지가 동일한 지 또는 동일한 객체인지 여부에 따라 다릅니다.

is동일한 객체가 아닌 동일한 객체인지 확인합니다. 작은 정수는 아마도 공간 효율성을 위해 동일한 메모리 위치를 가리키고 있습니다.

In [29]: a = 3
In [30]: b = 3
In [31]: id(a)
Out[31]: 500729144
In [32]: id(b)
Out[32]: 500729144

==임의 객체의 동등성을 비교 하는 데 사용해야 합니다. __eq____ne__속성을 사용하여 동작을 지정할 수 있습니다 .


OP와 같은 임의의 객체를 비교하는 방법을 실제로 설명하는 엄지 손가락!
Joooeey

54

늦었지만 답변이있는 소스를 원하십니까? 더 많은 사람들이 따라 할 수 있도록 입문 방식으로 시도하고 말하겠습니다.


CPython의 좋은 점은 실제로 소스를 볼 수 있다는 것입니다. 3.5 릴리스에는 링크를 사용 하지만 해당 2.x 링크를 찾는 것은 쉽지 않습니다.

CPython에서 새 오브젝트 작성을 처리 하는 C-API 함수 intPyLong_FromLong(long v)입니다. 이 기능에 대한 설명은 다음과 같습니다.

현재 구현은 -5에서 256 사이의 모든 정수에 대해 정수 객체 배열을 유지합니다.이 범위에서 int를 만들면 실제로는 기존 객체에 대한 참조를 다시 얻습니다 . 따라서 1의 값을 변경할 수 있어야합니다.이 경우 Python의 동작이 정의되지 않은 것 같습니다. :-)

(내 이탤릭체)

당신에 대해 모르지만 나는 이것을보고 생각합니다 : 그 배열을 찾아 봅시다!

CPython 구현하는 C 코드를 다루지 않았다면 ; 모든 것이 꽤 체계적이고 읽기 쉽습니다. 우리의 경우, 우리는에서 볼 필요가 Objects하위 디렉토리주요 소스 코드 디렉토리 트리 .

PyLong_FromLonglong객체를 다루 므로 내부를 들여다 볼 필요가 있다고 추론해서는 안됩니다 longobject.c. 내부를 살펴본 후 상황이 혼란 스럽다고 생각할 수 있습니다. 그들은 우리가 찾고있는 기능이 230 에서 차가워 져서 우리가 체크 아웃하기를 기다리고 있습니다. 작은 기능이므로 본체 (선언 제외)를 쉽게 붙여 넣을 수 있습니다.

PyObject *
PyLong_FromLong(long ival)
{
    // omitting declarations

    CHECK_SMALL_INT(ival);

    if (ival < 0) {
        /* negate: cant write this as abs_ival = -ival since that
           invokes undefined behaviour when ival is LONG_MIN */
        abs_ival = 0U-(unsigned long)ival;
        sign = -1;
    }
    else {
        abs_ival = (unsigned long)ival;
    }

    /* Fast path for single-digit ints */
    if (!(abs_ival >> PyLong_SHIFT)) {
        v = _PyLong_New(1);
        if (v) {
            Py_SIZE(v) = sign;
            v->ob_digit[0] = Py_SAFE_DOWNCAST(
                abs_ival, unsigned long, digit);
        }
        return (PyObject*)v; 
}

이제 우리는 C master-code-haxxorz 는 아니지만 멍청하지도 않습니다 CHECK_SMALL_INT(ival);. 우리는 이것이 이와 관련이 있다는 것을 이해할 수 있습니다. 확인 해보자:

#define CHECK_SMALL_INT(ival) \
    do if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) { \
        return get_small_int((sdigit)ival); \
    } while(0)

따라서 get_small_intival이 조건을 만족하면 함수를 호출하는 매크로입니다 .

if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS)

그래서 무엇 NSMALLNEGINTSNSMALLPOSINTS? 매크로! 여기 있습니다 :

#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS           257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS           5
#endif

우리의 상태는 if (-5 <= ival && ival < 257)call get_small_int입니다.

다음 get_small_int으로 모든 영광을 보자 (자, 우리는 단지 흥미로운 것들이있는 곳이기 때문에 몸을 볼 것입니다) :

PyObject *v;
assert(-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS);
v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];
Py_INCREF(v);

자, a를 선언 PyObject하고 이전 조건이 유지되고 할당을 실행한다고 주장하십시오.

v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];

small_ints우리가 찾고있는 배열과 매우 비슷합니다. 우리는 방금 문서를 읽을 수 있었고 모든 것을 알았을 것입니다! :

/* Small integers are preallocated in this array so that they
   can be shared.
   The integers that are preallocated are those in the range
   -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
*/
static PyLongObject small_ints[NSMALLNEGINTS + NSMALLPOSINTS];

그래,이 사람은 우리 남자 야 int범위에서 새 항목을 만들려면 [NSMALLNEGINTS, NSMALLPOSINTS)이미 할당 된 기존 개체에 대한 참조를 다시 가져옵니다.

참조는 동일한 객체를 참조하므로 id()직접 발행 하거나 ID를 확인 is하면 정확히 동일한 결과가 반환됩니다.

그러나 언제 할당됩니까?

_PyLong_Init파이썬 에서 초기화하는 동안 기꺼이 for 루프에 들어가면 다음과 같이하십시오.

for (ival = -NSMALLNEGINTS; ival <  NSMALLPOSINTS; ival++, v++) {

루프 바디를 읽으려면 소스를 확인하십시오!

내 설명으로 C 물건을 명확하게 만들었 기를 바랍니다.


그러나 257 is 257? 뭐야?

이것은 실제로 설명하기가 더 쉬우 며 이미 그렇게 시도했습니다 . 파이썬 이이 대화 형 문을 단일 블록으로 실행한다는 사실 때문입니다.

>>> 257 is 257

이 문장을 완성하는 동안 CPython은 두 개의 일치하는 리터럴이 있으며 동일한 PyLongObject표현 을 사용한다는 것을 알 수 있습니다 257. 컴파일을 직접 수행하고 내용을 검사하면 이것을 볼 수 있습니다.

>>> codeObj = compile("257 is 257", "blah!", "exec")
>>> codeObj.co_consts
(257, None)

CPython이 작업을 수행하면 이제 정확히 동일한 객체를로드합니다.

>>> import dis
>>> dis.dis(codeObj)
  1           0 LOAD_CONST               0 (257)   # dis
              3 LOAD_CONST               0 (257)   # dis again
              6 COMPARE_OP               8 (is)

그래서 is돌아갑니다 True.


37

소스 파일 intobject.c를 체크인 할 수 있으므로 , 파이썬은 효율성을 위해 작은 정수를 캐시합니다. 작은 정수에 대한 참조를 만들 때마다 새 객체가 아닌 캐시 된 작은 정수를 참조합니다. 257은 작은 정수가 아니므로 다른 객체로 계산됩니다.

==그 목적 으로 사용 하는 것이 좋습니다.


19

나는 당신의 가설이 맞다고 생각합니다. id(객체의 동일성)으로 실험 :

In [1]: id(255)
Out[1]: 146349024

In [2]: id(255)
Out[2]: 146349024

In [3]: id(257)
Out[3]: 146802752

In [4]: id(257)
Out[4]: 148993740

In [5]: a=255

In [6]: b=255

In [7]: c=257

In [8]: d=257

In [9]: id(a), id(b), id(c), id(d)
Out[9]: (146349024, 146349024, 146783024, 146804020)

숫자 <= 255는 리터럴로 취급되며 위의 내용은 다르게 취급됩니다!


1
시작 시간에 -5에서 +256 사이의 값을 나타내는 개체가 만들어지기 때문에 해당 값을 모두 사용하여 미리 작성된 개체를 사용하기 때문입니다. 해당 범위 밖의 정수에 대한 거의 모든 참조는 참조 될 때마다 새로운 내부 객체를 만듭니다. 리터럴이라는 용어의 사용은 혼란 스럽습니다. 리터럴은 일반적으로 코드에 입력 된 모든 값을 나타냅니다. 따라서 소스 코드의 모든 숫자는 리터럴입니다.
Tony Suffolk 66

13

정수, 문자열 또는 날짜 시간과 같은 변경 불가능한 값 객체의 경우 객체 ID가 특히 유용하지 않습니다. 평등에 대해 생각하는 것이 좋습니다. 아이덴티티는 본질적으로 가치 객체에 대한 구현 세부 사항입니다. 불변이기 때문에 동일한 객체 또는 여러 객체에 대한 다중 참조를 갖는 것 사이에는 효과적인 차이가 없습니다.


12

기존 답변에서 지적되지 않은 또 다른 문제가 있습니다. 파이썬은 두 개의 불변 값을 병합 할 수 있으며 미리 작성된 작은 int 값이 이것이 일어날 수있는 유일한 방법은 아닙니다. 파이썬 구현은 이것을 보장 하지는 않지만 작은 정수 이상의 것을 위해 모두 수행합니다.


한 가지 들어, 빈 같은 다른 미리 만들어진 가치가있다 tuple, str그리고 bytes, 일부 짧은 문자열 (CPython과 3.6, 그것은 256 단일 문자 라틴어 1 문자열이다). 예를 들면 다음과 같습니다.

>>> a = ()
>>> b = ()
>>> a is b
True

또한 사전 생성되지 않은 값도 동일 ​​할 수 있습니다. 다음 예를 고려하십시오.

>>> c = 257
>>> d = 257
>>> c is d
False
>>> e, f = 258, 258
>>> e is f
True

그리고 이것은 int값으로 제한되지 않습니다 .

>>> g, h = 42.23e100, 42.23e100
>>> g is h
True

분명히 CPython에는에 대한 사전 생성 된 float값이 제공되지 않습니다 42.23e100. 무슨 일이야?

CPython의 컴파일러와 같은 일부 알려진 종류의 불변 상수 값을 통합한다 int, float, str, bytes, 동일한 편집 단위이다. 모듈의 경우 전체 모듈은 컴파일 단위이지만 대화식 인터프리터에서 각 명령문은 별도의 컴파일 단위입니다. cd별도의 문으로 정의 되므로 해당 값이 병합되지 않습니다. 이후 ef같은 문에 정의되어, 그 값이 병합됩니다.


바이트 코드를 디스 어셈블하면 현재 진행중인 작업을 확인할 수 있습니다. 수행하는 함수를 정의한 e, f = 128, 128다음 호출 dis.dis하면 상수 값이 하나 있음을 알 수 있습니다.(128, 128)

>>> def f(): i, j = 258, 258
>>> dis.dis(f)
  1           0 LOAD_CONST               2 ((128, 128))
              2 UNPACK_SEQUENCE          2
              4 STORE_FAST               0 (i)
              6 STORE_FAST               1 (j)
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE
>>> f.__code__.co_consts
(None, 128, (128, 128))
>>> id(f.__code__.co_consts[1], f.__code__.co_consts[2][0], f.__code__.co_consts[2][1])
4305296480, 4305296480, 4305296480

128바이트 코드에서 실제로 사용하지 않더라도 컴파일러가 상수로 저장 되어 CPython의 컴파일러가 얼마나 적은 최적화를했는지 알 수 있습니다. 즉, 비어 있지 않은 튜플은 실제로 병합되지 않습니다.

>>> k, l = (1, 2), (1, 2)
>>> k is l
False

함수에 그것을 넣어, dis그것은, 그리고 봐 co_consts저기는의 12,이 (1, 2)같은 공유 튜플 1하고 2있지만, 동일하지 않은, 그리고 ((1, 2), (1, 2))두 개의 별개의 동일한 튜플을 가지고 튜플.


CPython이 수행하는 최적화는 문자열 인터 닝입니다. 컴파일러 상수 폴딩과 달리 소스 코드 리터럴로 제한되지 않습니다.

>>> m = 'abc'
>>> n = 'abc'
>>> m is n
True

반면, str유형 및 내부 스토리지 유형 "ascii compact", "compact"또는 "legacy ready"의 문자열로 제한 되며 많은 경우 "ascii compact"만 구속됩니다.


어쨌든 어떤 값이되어야하는지 또는 구별 할 수 없는지에 대한 규칙은 구현마다, 그리고 동일한 구현의 버전간에, 그리고 심지어 동일한 구현의 동일한 사본에서 동일한 코드의 실행 간에도 다양합니다. .

재미를 위해 특정 Python에 대한 규칙을 배우는 것이 좋습니다. 그러나 코드에서 그것들에 의존 할 가치는 없습니다. 유일한 안전한 규칙은 다음과 같습니다.

  • 두 동일하지만 별도로 만들어 불변의 값을 사용하지 않는 (동일 가정 쓰지 코드 수행 x is y, 사용x == y )
  • 동일하지만 별도로 생성 된 불변 값이 서로 다른 것으로 가정하는 코드를 작성하지 마십시오 (use x is not y, use x != y)

즉, is문서화 된 싱글 톤 (예 :)을 테스트 None하거나 코드의 한 위치 (예 : _sentinel = object()관용구) 에서만 생성 된 테스트 에만 사용 하십시오 .


덜 비밀스러운 조언은 간단 x is y합니다 x == y. 비교 하는 데 사용하지 마십시오 . 마찬가지로 사용하지 않는 x is not y, 사용x != y
SMCI

따라서이 질문을 보면 왜 a=257; b=257한 줄에 a is bTrue
Joe

8

is 아이덴티티 항등 연산자 (같은 기능 id(a) == id(b)); 단지 두 개의 동일한 숫자가 반드시 동일한 객체 일 필요는 없습니다. 성능상의 이유로 일부 작은 정수는메모리에 저장 되므로 동일한 경향이 있습니다 (불변이기 때문에 수행 할 수 있음).

===반면 PHP의 연산자 x == y and type(x) == type(y)는 Paulo Freitas의 의견에 따라 평등과 유형을 확인하는 것으로 설명됩니다 . 일반적인 숫자로는 충분하지만 터무니없는 방식으로 is정의 __eq__된 클래스 와는 다릅니다 .

class Unequal:
    def __eq__(self, other):
        return False

PHP는 분명히 "내장"클래스에 대해 동일한 것을 허용합니다 (PHP가 아닌 C 레벨에서 구현됨을 의미합니다). 약간 덜 불합리한 사용은 타이머 객체로, 숫자로 사용될 때마다 다른 값을 가질 수 있습니다. 왜 Visual Basic을 모방하고 싶습니까?Nowtime.time() 알지 못하는 평가인지를 보여주는 대신

Greg Hewgill (OP)은 "제 목표는 가치의 평등보다는 객체의 정체성을 비교하는 것입니다. 숫자를 제외하고는, 객체의 정체성을 가치의 평등과 같은 것으로 취급하고자하는 숫자는 제외합니다."

우리는 우리가 비교할지 여부를 선택, 숫자 또는하지 물건을 분류해야 이것은 또 다른 대답을 할 것이다 ==is. CPython숫자 프로토콜을 정의합니다 PyNumber_Check를 포함 하지만 Python 자체에서는 액세스 할 수 없습니다.

우리는 isinstance우리가 아는 모든 숫자 유형과 함께 사용하려고 시도 할 수 있지만, 이것은 불완전 할 것입니다. types 모듈에는 StringTypes 목록이 있지만 NumberTypes는 없습니다. 파이썬 2.6부터 내장 클래스는 기본 클래스 numbers.Number를 가지고 있지만 같은 문제가 있습니다.

import numpy, numbers
assert not issubclass(numpy.int16,numbers.Number)
assert issubclass(int,numbers.Number)

그건 그렇고, NumPy 는 낮은 숫자의 개별 인스턴스를 생성합니다.

나는 실제로이 질문의 변형에 대한 답을 모른다. 이론적으로 ctypes를 사용하여 호출 할 수 있다고 가정 PyNumber_Check하지만 해당 기능조차도 토론되었습니다. 확실히 이식성이 없습니다. 우리는 지금 우리가 테스트하는 것에 대해 덜 구체적이어야합니다.

결국이 문제는 원래 Scheme 's number? 또는 Haskell의 유형 클래스 Num 과 같은 술어가있는 유형 트리가없는 Python에서 비롯됩니다 . is값 평등이 아닌 객체 동일성을 검사합니다. PHP는 화려한 역사를 가지고 있습니다 . PHP5의 객체 는 아니지만 PHP4의 객체에서만 ===동작 합니다. 언어 (버전을 포함하여)를 가로 질러 이동함에 따라 점점 더 많은 고통이 있습니다.is


4

문자열에서도 발생합니다.

>>> s = b = 'somestr'
>>> s == b, s is b, id(s), id(b)
(True, True, 4555519392, 4555519392)

이제 모든 것이 괜찮아 보입니다.

>>> s = 'somestr'
>>> b = 'somestr'
>>> s == b, s is b, id(s), id(b)
(True, True, 4555519392, 4555519392)

그것도 예상됩니다.

>>> s1 = b1 = 'somestrdaasd ad ad asd as dasddsg,dlfg ,;dflg, dfg a'
>>> s1 == b1, s1 is b1, id(s1), id(b1)
(True, True, 4555308080, 4555308080)

>>> s1 = 'somestrdaasd ad ad asd as dasddsg,dlfg ,;dflg, dfg a'
>>> b1 = 'somestrdaasd ad ad asd as dasddsg,dlfg ,;dflg, dfg a'
>>> s1 == b1, s1 is b1, id(s1), id(b1)
(True, False, 4555308176, 4555308272)

이제 예상치 못한 일입니다.


이 일이 생겨났다-동의했다. 그래서 나는 그것을 가지고 놀았고, 우주와 관련하여 더 이상합니다. 예를 들어, 문자열 'xx'은 예상대로 'xxx'이지만 'x x'그렇지 않습니다.
Brian

2
공간이 없으면 기호처럼 보이기 때문입니다. 이름은 자동으로 인터 닝되므로 xx, 파이썬 세션의 어느 곳에 라도 이름이 지정된 것이 있으면 해당 문자열은 이미 인터 닝됩니다. 이름과 비슷한 경우 휴리스틱이있을 수 있습니다. 숫자와 마찬가지로 이것은 불변이기 때문에 가능합니다. docs.python.org/2/library/functions.html#intern guilload.com/python-string-interning
Yann Vernier

3

Python 3.8의 새로운 기능 : Python 동작의 변경 사항 :

신원 확인 ( 및 )을 특정 유형의 리터럴 (예 : 문자열, 정수)과 함께 사용 하면 컴파일러에서 SyntaxWarning이 생성 됩니다. 이들은 종종 CPython에서 우연히 작동 할 수 있지만 언어 사양에 따라 보장되지는 않습니다. 경고는 대신 사용자에게 동등성 테스트 ( 및 ) 를 사용하도록 권장 합니다.isis not==!=

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