TIL - Python - 가변객체&불변객체

김영훈·2021년 2월 25일
1

Python

목록 보기
8/14

# 객체(object)

  • 객체(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에 따라 분류 가능하다는 사실도 짐작할 수 있었다. 여기까지가 내가 알아본 파이썬의 객체에 대한 얄팍한 지식이다.

# 가변객체 vs 불변객체

  • 가변(mutable)과 불변(immutable) 역시 파이썬의 수많은 객체를 분류하는 기준으로 이해할 수 있을 것이다. 두 개념에 대한 구체적인 설명은 검색을 통해 찾을 수 있었다.

    <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값을 가지고 있다. id()를 통해 예시의 객체 l1l2의 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    
  • 할당 연산자가 사용되기 전과 후로 나눠 객체의 id값을 확인했다. 확인 결과 l1l2 모두 값이 할당되기 이전과 이후의 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  
  • 할당 연산자가 사용되기 전과 후의 id값이 모두 동일하다. 이 경우엔 기존 객체의 값이 변경됐음을 의미한다.

# 개인적인 단상 정리

  • 불변객체인덱싱을 활용한 값 변경, 삭제 등이 불가능하다.

  • 불변객체할당 연사자(+=, -=, *=, /=)를 활용하여 확장시키면, 기존 객체와 다른 새로운 객체가 생성된다.

profile
Difference & Repetition

0개의 댓글