여기도 추상화

프로그래밍 언어를 학습하는 과정에서 객체(object)는 난감한 존재다. 왜냐하면, 딱 잘라서 말하기 쉽지 않고 그렇다고 두리뭉실하게 넘기기엔 중요한 용어(term)다. 프로그래밍 언어에서 제공하는 공식 문서를 꼭 참고해야 될 만큼 용어의 범위가 넓고 난해하다.

Python의 객체는 공식 문서에서 정의하길 상태(state)와 행위(behavior)를 가지는 데이터로 정의한다.

Any data with state (attributes or value) and defined behavior (methods). Also the ultimate base class of any new-style class. Glossary

그리고 또 다른 정의로 추상화된 데이터로 정의하며, 데이터는 객체 또는 객체 간의 관계를 표현한다고 정의한다.

Objects are Python’s abstraction for data. All data in a Python program is represented by objects or by relations between objects. (In a sense, and in conformance to Von Neumann’s model of a “stored program computer”, code is also represented by objects.) 3.1. Objects, values and types

따라서 Python의 객체는 상태와 행위를 가지는 추상화된 데이터다. 따라서 'stored program computer'의 원칙에 따라 '추상화된 데이터'가 '코드'이며, 이 코드는 메모리에서'만' 작동한다.

상태와 행위

Python의 관점에서 메모리에 존재하는 모든 것은 객체다. 또한 Python 코드로 표현 가능한 모든 것은 객체다. 코드는 상태와 행위를 정의하므로 객체는 상태와 행위를 가진다. 마지막으로 객체는 다른 객체와 연결된다.

이러한 단순한 논리를 따라가보면 결론적으로 상태와 행위를 가지는 객체는 메모리에 위치하며, 또한 객체는 다른 객체와 연결될 수 있다. 따라서 모든 객체는 각자의 상태를 가지므로 객체를 구별 할 수 있는 고유값이 필요하다. 만약, 개별 객체를 구별할 수 없다면 상태와 행위에 관련된 내용은 무의미하다.

id와 type

Python의 모든 객체는 메모리에 생성 된 후에 부여받은 자신만의 고유값을 id라 하고 id는 변경되지 않는다. id는 메모리에 위치한 객체의 '고유값(혹은 주소)'이기 때문이다. Python에서 자주 사용하는 비교 연산자 중에서 is 연산자는 두 객체의 id를 비교하는 것으로 id() 함수는 객체의 id를 정수로 반환한다.

객체가 메모리에 존재 한다는 것은 메모리를 차지하고 있다는 것이다. 객체의 메모리의 저장되어 있으며, 저장된 크기가 얼마인지 알고 있어야 한다. 더 나아가 객체는 자신이 의미하는 값을 해석하는 방법도 제공해야 한다. id가 메모리의 위치를 나타낸다면, type은 값을 구성하는 형태와 메모리의 크기를 나타낸다. 그리고 type을 통해서 객체의 값을 해석하는 방법도 알 수 있다.

id와 type은 객체가 메모리에 위치한 고유주소와 메모리 공간의 크기와 표현을 나타낸다. 즉, 객체는 상태와 행위를 가지고 있기 때문에 메모리 공간에 객체의 상태와 행위를 표현하기 위해선 별도의 방식이 필요하다. 예를 들어서 '2바이트는 상태를 표현하고, 4바이트는 행위를 표현한다'와 같은 메모리에 존재하는 객체를 올바로 이해하기 위해선 객체가 차지하고 있는 메모리를 읽어낼 수 있어야 한다. 따라서 id와 type은 객체의 고유한 특징이다.

type

우리에겐 2가지 선택권이 주어진다. Python에서 미리 정의된 type만 사용하거나, 우리에게 필요한 type을 만들어서 사용하는 것이다. 대부분의 프로그래밍 언어는 미리 정의된 type을 제공한다. 정수, 실수, 유리수, 문자, 문자열 등이 대표적인 정의된 type이라 할 수 있다. 객체가 가질수 있는 상태와 행위를 미리 정의해서 개발자가 정의된 객체를 기반으로 프로그램을 진행한다. 반면 C언어의 struct(구조체), C++의 class(클래스)는 사용자가 type을 만들 수 있는 방법을 제공한다. 정확히는 객체의 상태와 행위를 정의할 수 있는 방식을 제공한다. 우리가 프로그래밍을 한다는 것은 객체의 type을 결정한다고 해도 과언이 아니다.

변수는 객체가 아니기 때문에, 객체를 참조할 수 있다.

흔한 개발자의 마인드로 아래 코드를 곰곰히 생각해보면 1) 객체가 만들어지고 2) 변수가 선언되고 3) 변수에 객체가 할당되는게 합리적인 것 같다. 그런데, 변수에 객체를 할당한다는 것이 무슨 뜻일까? Python의 모든 것은 객체라고 하는데 변수도 객체인가? Python을 잘 사용하던 어느 날, 갑자기 '변수는 객체인가?'라는 막연한 의문이 머리속을 스쳐갔다. Python에서 객체를 할당한다는 것이 무슨 뜻일까? 이런 의문을 스스로 해결한 어느 날, 막연히 레벨업된 느낌이 들었다.

str = "hello!"

str이란 변수가 'hello!'라는 객체를 할당하는 것에 대한 궁금증의 내면은 C언어에서 배웠던 변수의 흔적이 살아서 머리를 혼탁하게 하기 때문이다. 당연히 'hello!'라는 객체를 str에 복사할 것이라는 생각, 하지만 str이 객체라면 객체가 다른 객체를 복사해야 할 이유가 무엇인가? 계속되는 의문은 처음 배운 언어에서 당연시 했던 '할당'이란 의미 때문이다.

Python에서 변수는 객체와 연결을 위한 장치다. 우리가 C나 C++에서 배웠던 '변수'와 약간의 차이가 있다. 변수가 객체와 연결을 위한 장치이기 때문에 변수엔 type이 정의되지 않아야 한다. 당연하게도 변수는 모든 객체와 연결할 수 있어야 한다. 따라서 모든 변수는 어떤 type으로 한정되어서는 안된다. 왜냐하면 모든 객체와 연결되어야 하기 때문이다. 따라서 변수는 객체가 아니며, 객체가 아니기 때문에 type을 가질 수 없다. 그리고 변수가 객체를 연결하는 과정을 reference(참조)라고 한다.

정리하면 변수는 객체를 참조하기 위한 항목으로 객체는 아니다. 변수는 타입을 가질 수 없고, 그렇기 때문에 모든 객체를 참조할 수 있다. 우리가 변수에 대해서 말할 수 있는 모든 것은 해당 변수가 특정 객체를 참조하고 있거나 그렇지 않다는 것이다. 이런 간단한 논리를 이해하지 못하면 Python의 동작이 혼란스럽다.

다음은?

이쯤되면 참조에 대해서 궁금한게 한 두 가지가 아닐 것이다. 이제 참조에 대해서 알아보기 위해서 '문자열'로 시작해서 '자료구조'로 향해보자.