객체(object)는 파이썬을 다루기 위해 필수적으로 알아둬야 할 개념이다. 파이썬 프로그램 내부의 모든 데이터들이 객체 형태로 표현되기 때문이다. 그렇다면 데이터가 객체 형태로 표현된다는 것은 무슨 뜻일까? 이해의 실마리를 파이썬 용어 설명집에서 찾아봤다.
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.)
영어에 조예가 깊지 않은 나로서는 몇 가지 눈에 띄는 어휘를 중심으로 의미를 추측하는 수밖에 없었다. 가장 먼저 나의 눈을 사로잡았던 어휘는 "추상화(abstraction)"였다. 보통 추상화는 구체적으로 설명하거나 표현하기 어려운 복잡한 대상들을 하나의 공통적인 개념으로 일반화시켜 표현하는 것을 의미한다. 정확한 의미를 추측하긴 어려우나, 파이썬에서 객체란 우리 주변의 복잡한 데이터들을 하나의 개념으로 일반화하여 표현하는 것을 의미하는 것 같다.(솔직히 잘 모르겠다ㅠㅠ) 어찌 됐든 파이썬에서 다루는 수많은 데이터가 객체라는 개념을 통해서 표현되는 것 만큼은 분명해 보인다.
보다 구체적인 단서는 아래의 설명에서 찾을 수 있었다.
Every object has an identity, a type and a value. An object’s identity never changes once it has been created;
모든 객체에는 identity, type, value가 담겨있다고 한다. 관련 정보를 조금 더 찾아본 결과, 파이썬 함수 id()
, type()
를 통해 출력되는 값이 각각 identity(id)와 type에 해당한다는 것을 알 수 있었다. 게다가 id값의 경우 한번 생성되면 수정이 불가능한 고유의 값이라고 하니, 이쯤 되면 파이썬의 모든 데이터는 객체로 표현될 뿐만 아니라, 각각의 객체를 고유의 id값이나 type에 따라 분류 가능하다는 사실도 짐작할 수 있었다. 여기까지가 내가 알아본 파이썬의 객체에 대한 얄팍한 지식이다.
<mutable과 immutable>
파이선은 모든 것이 객체(object)인데, 그 속성이 mutable(값이 변한다) 과 immutable로 구분된다.
Immutable
: 숫자(number), 문자열(string), 튜플(tuple)
Mutable
: 리스트(list), 딕셔너리(dictionary), NumPy의 배열(ndarray)
즉, 숫자, 문자열, 튜플은 값을 변경하지 못하고, 리스트와 딕셔너리는 변경할 수 있다는 뜻이다.
l1 = (10, 15, 20) # 불변 객체 튜플(tuple)
l2 = 'string' # 불변 객체 문자열(string)
l1[0] = 5 # 인덱싱을 통해 값을 변경
l2[0] = 'x'
# 결괏값: TypeError 발생
# TypeError: 'str' object does not support item assignment
m1 = [10, 15, 20] # 가변 객체 리스트(list)
m2 = {'x': 10, 'y': 15, 'z': 20} # 가변 객체 딕셔너리(dict)
m1[0] = 5 # 인덱싱을 통해 값을 변경
m2['x'] = 5 # 딕셔너리는 인덱싱 대신 key를 사용하여 값을 변경
print(m1) # 결괏값: [5, 15, 20]
print(m2)assignment # 결괏값: {'x': 5, 'y': 15, 'z': 20}
m1 = [10, 15, 20]
m2 = {'x': 10, 'y': 15, 'z': 20}
m1 += [30] # 할당 연산자 '+='를 통해 값을 변경
m2.update({'w':30}) # 딕셔너리는 할당 연사자 사용이 불가능하므로 update() 활용
print(m1) # 결괏값: [5, 15, 20, 30]
print(m2) # 결괏값: {'x': 5, 'y': 15, 'z': 20, 'w': 30}
l1 = (10, 15, 20)
l2 = 'string'
l1 += (30,) # 할당 연산자 '+='를 통해 값을 변경
l2 += ' text'
print(m1) # 결괏값: (10, 15, 20, 30)
print(m2) # 결괏값: string text
id()
를 통해 예시의 객체 l1
과 l2
의 id값을 확인해보면, 실제로 객체의 값이 변경됐는지를 확인할 수 있다.
l1 = (10, 15, 20)
l2 = 'string'
print(id(l1)) # 결괏값: 1326246306240
print(id(l2)) # 결괏값: 1326246509616
l1 += (30,)
l2 += ' text'
print(id(l1)) # 결괏값: 1326246537392
print(id(l2)) # 결괏값: 1326246613040
l1
과 l2
모두 값이 할당되기 이전과 이후의 id값이 다르다. id값이 다르다는 것은 두 객체가 동일한 객체가 아니라는 것을 뜻한다. 결론적으로, 값이 변한 것이 아니라 새로운 객체가 생성된 것임을 확인할 수 있다. 그렇다면 가변객체의 경우는 어떨까??
m1 = [10, 15, 20]
m2 = {'x': 10, 'y': 15, 'z': 20}
print(id(m1)) # 결괏값: 2348885421824
print(id(m2)) # 결괏값: 2348885542848
m1 += [30]
m2.update({'w':30})
print(id(m1)) # 결괏값: 2348885421824
print(id(m2)) # 결괏값: 2348885542848
불변객체는 인덱싱을 활용한 값 변경, 삭제 등이 불가능하다.
불변객체를 할당 연사자(+=, -=, *=, /=)를 활용하여 확장시키면, 기존 객체와 다른 새로운 객체가 생성된다.