#동일한 객체를 참조하는 charles 와 lewis
charles = {'name': 'Charles L. Dodgson', 'born': 1832}
lewis = charles
lewis is charles
id(charles), id(lewis)
lewis['balance'] = 950
#alex와 charles를 비교하면 같지만, alex가 charles는 아니다
ales = {'name': 'Charles L. Dodgson', 'born': 1832m 'balance':950}
alex == charles
alex is not charles
코드 안에서 lewis와 charles는 별명으로 두 변수가 동일 객체에 바인딩되어 있고, alex는 charles에 대한 별명이 아니고, 이 두 변수는 서로 다른 객체에 바인딩되어 있다. alex에 바인딩된 객체와 charles에 바인딩된 객체가 동일한 값을 갖고 있으므로 == 연산자(동치 연산자)에 의해 동일 판단이 되지만 정체성은 다르다.
모든 객체는 정체성, 자료형, 값을 갖고 객체의 정체성은 일단 생성한 후에는 결코 변경되지 않는다. 정체성은 메모리 내의 객체 주소라고 생각할 수 있고, is 연산자는 두 객체의 정체성을 비교하므로, id()함수는 정체성을 나타내는 정수를 반환한다.
객체 ID의 실제 의미는 구현에 따라 다른데, CPython의 경우 id()는 객체의 메모리 주소를 반환하지만 다른 파이썬 인터프리터는 메모리 주소 이외의 다른 값을 반환하지만 ID는 객체마다 고유한 레이블이라는 것을 보장하며 객체가 소멸될 때까지 결코 변하지 않는다는 점이 핵심이다.
그리고, 실제 프로그래밍할 때는 id() 함수를 거의 사용하지 안고 정체성 검사는 주로 is 연산자를 이용해서 수행하며 ID를 직접 비교하지 않는다.
== 연산자가 객체의 값을 비교한다면, is 연산자는 객체의 정체성을 비교한다.
정체성보다 값을 비교하는 경우가 많기에 ==연산자를 자주 볼 수 있다.
그러나, 변수를 싱글턴과 비교할 때는 is 연산자를 사용하고, 이때 변수를 None과 비교하는 것이 일반적이다.
x is None
x is not None
is연산자는 오버로딩할 수 없으므로 파이썬이 이 값을 평가하기 위해 특별 메서드를 호출할 필요가 없고, 두 정수를 비교하는 정도로 연산이 간단하므로 is 연산자가 더 빠르다.
그러나, a == b는 a.eq(b)의 편리구문이다. object 객체에서 상속받은eq() 메서드는 객체의 ID를 비교하므로 is 연산자와 동일한 결과를 내지만 오버라이드해서 객체의 값을 비교한다.
그러므로, 대형컬렉션이나 깊이 내포된 구조체를 비교하는 경우 동치 비교를 상당히 많이 한다.
리스트, 딕셔너리, ㅈ집합 등 대부분의 파이썬 컬렉션과 마찬가지로 튜플도 객체에 대한 참조를 담는다
참조된 항목이 가변형이면 튜플 자체는 불변형이지만 참조된 항목은 변할 수 있고, 튜플의 불편성은 tuple 데이터 구조체의 물리적인 내용만을 말하는 것이며 참조된 객체까지 불변서응ㄹ 가지는 것은 아니다.
#초기 t1과 t2는 동일하지만, t1 튜플 안에 있는 가변 항목을 변경하면 달라진다.
t1 = (1,2,[30,40])
t2 = (1,2,[30,40])
t1 == t2
id(t1[-1])
t1[-1].append(99)
t1
id(t1[-1])
t1 == t2
동질성과 정체성 간의 차이는 객체를 복사할 때 더 큰 영향을 미치고, 사본은 ID가 다른 동일한 객체지만 객체가 다른 객체를 담고 있을 때 복사하면 내부 객체도 복사해야 할까? 아니면 내부 객체는 공유해도 될까? 정답은 없다.