2011년 4월 28일 목요일

[Python] 'is' 커맨드

Python의 비교문에는 '=='(equal), '!='(not equal) 외에도 'not'과 'is'가 있다. 이 중 not은 말 그대로 False일 때 True가 되게 만드는 연산자일 뿐.

자 그럼 남아있는 'is'를 한번 확인해 보자.
>>> a = 1
>>> b = 1
>>> a is b
True
>>> a = "123"
>>> b = "123"
>>> a is b
True
의미와 비슷하게 '=='와 동일한 동작을 하고 있다.
>>> a = None
>>> b = None
>>> a is b
True
역시 예상했던 예다.

하지만...
>>> a = "this is string. this is string this is string"
>>> b = "this is string. this is string this is string"
>>> a is b
False
어라 결과가 다르다! 혹시 문자열을 좀 잘못 입력했을까?
>>> a = "-1--1-1-1-1-1-1-1"
>>> b = "-1--1-1-1-1-1-1-1"
>>> a is b
False
역시 다르다고 나온다.

여기서 'is'가 '=='과 어떤 점이 다른지를 이해해야 한다.
1. 'is'는 비교 연산자가 맞다. 하지만 중요한 건 변수의 값을 비교하는게 아니라 레퍼런스(C식으로 설명하자면 변수의 포인터)를 비교하는 연산자다.
2. Python의 변수는 내부적으로 데이터 그 자체를 가리키는게 아니라 인스턴스 포인터(값이 저장되어 있는 메모리의 주소, 즉 레퍼런스)를 가리킨다. 물론 C언어가 아니기 때문에 변수를 포인터처럼 엑세스 할 수는 없다.
그런데 위 두 가지 이유가 왜 False가 발생한 예제를 설명할 수 있는가.

한가지 설명을 더 추가해야 한다.
3. Python은 자주 쓰이는 값이 미리 정의되어 있다. 즉, 위에서 숫자 1이나 문자열 "123" 은 자주 쓰이는 데이터다. 이런 데이터는 이미 메모리상에 고정되어서 저장되어 있고 프로그래머가 이 데이터를 이용하려 할 때 새로운 데이터 공간이 할당되는게 아니라 미리 할당되어 있는 '자주 쓰이는 데이터가 저장된 공간'의 포인터를 매칭시키게 된다.
이제 실패한 예의 긴 문자열은 왜 'is'로 비교에 실패하는지 알 수 있다. 긴 문자열을 변수에 대입시키면 새로운 메모리 공간이 할당되어서 여기에 문자열이 저장되고 변수는 이 문자열이 저장된 포인터(memory address)를 저장한다. 두 변수는 각자 새로운 메모리 영역을 할당받아서 그 곳의 포인터를 저장하고 있다. 이 경우 둘 사이의 포인터가 다르므로 'is' 연산의 결과는 False가 된다.

반대로 1, "123", None 등의 값은 상수이거나 자주 쓰이는 값이어서 언제나 동일한 포인터가 할당된다. 따라서 'is'의 연산 결과는 True가 된다.

이러한 이유로, 'is' 연산자는 포인터(레퍼런스)를 비교하는 연산자이지, 데이터를 비교하는 연산자가 아니라는 점을 설명할 수 있다.

자 정리해보자: 'is' 커맨드는 가급적이면 상수(None, True, False 등)를 비교하는데만 쓰도록 하자. 예를 들자면 varA is not None 이런 식으로...

뭐 아는 사람은 다 알겠지만 -_- 스크립트 언어라고 포인터 개념을 무시하면 낭패보는거다. 므흣~

댓글 없음 :