Python 변수, 객체, 복사


Python 변수, 객체, 복사에 대해 작성한 글입니다


이야기할 내용들

  • 파이썬 변수를 은유적 표현
    • 변수는 이름표지 상자가 아님
  • 객체의 정체성, 동질성, 별명
    • 튜플은 불변형이지만 그 안에 있는 값은 바뀔 수 있음
    • 얕은 복사, 깊은 복사

변수는 상자가 아니다

  • 많은 책에서 변수를 상자로 표현하고 있는데, 객체지향 언어에서는 이 개념이 참조 변수를 이해하는데 방해가 됨
  • 변수는 객체에 붙은 레이블이라고 생각하는 것이 좋음
  • 상자로서의 변수를 설명할 수 없는 코드

      a = [1, 2, 3]
      b = a
      a.append(4)
      b
      >>> [1,2,3,4
    
  • 변수를 상자로 생각하지 말고 변수를 포스트잇으로 생각하자
  • 할당문의 경우 오른쪽 부분이 먼저 실행된다
    • 객체가 생성된 후, 변수가 객체에 할당됨
      class Gizmo:
          def __init__(self):
              print("Gizmo id : " f'{id(self})
    	
      x = Gizmo()
      >>> Gizmo id : 431489152
      y = Gizmo() * 10
      >>> Gizmo id : 431489321
      TypeError: unsupported operand type(s) for *: 'Gizmo' and 'int'
    
    • 할당문의 오른쪽에서 객체를 생성한 후, 레이블을 붙이듯 할당문 왼쪽에 있는 변수가 객체에 바인딩
    • 여러 레이블을 붙이는 것을 별명이라 생각하기

정체성, 동질성, 별명

  • 동일한 객체를 참조하는 사례

      byeon = {'name': 'Seongyun', 'born': 1990}
      kyle = byeon
      kyle is byeon
      >>> True
      id(kyle), id(byeon)
      >>> (430048989, 430048989)
      kyle['age'] = 29
      byeon
      >>> {'name': 'Seongyun', 'born': 1990, 'age': 29}
    
  • 값은 같으나 정체성이 다른 사례

      byeonzzi = {'name': 'Seongyun', 'born': 1990, 'age': 29}
      byeonzzi == byeon
      >>> True
      byeonzzi is not byeon
      >>> True
    
    • byeonzzi와 byeon은 동일한 값을 가지고 있기 때문에 == 연산자에 의해 동일하다고 판단
    • 하지만 정체성은 다름. a is not b는 두 객체의 정체성이 다르다고 표현하는 파이썬 방식
    • 정체성은 메모리 내의 객체 주소라 생각할 수 있으며 is 연산자가 두 객에츼 정체성을 비교
    • id() 함수로 정체성의 id를 반환. 실제 프로그래밍에선 주로 is 연산자 사용
  • == 연산자와 is 연산자의 선택
    • 값을 비교하는 경우엔 == 사용이 더 많이 보임
    • 그러나 변수를 싱글턴과 비교할 땐 is 연산자 사용
    • is 연산자는 오버로딩할 수 없고, 연산이 간단해서 속도가 빠름
    • a == b는 a.__eq__(b)의 편리 구문
  • 튜플의 상대적 불변성
    • 참조된 항목이 가변형이면 튜플 자체는 불변형이지만 참조된 항목은 변할 수 있음
      t1 = (1, 2, [30, 40])
      t2 = (1, 2, [30, 40])
      t1 == t2
      >>> True
      id(t1[-1])
      >>> 4302515784
      t1[-1].append(99)
      t1
      >>> (1, 2, [30, 40, 99])
      id(t1[-1])
      >>> 4302515784
      t1 == t2
      >>> False
    
    • 위 사례를 보면 정체성(id)은 동일하지만 값은 변경됨

기본 복사는 얕은 복사

  • 복사하는 가장 쉬운 방법은 자료형 자체의 내장 생성자를 사용하는 것
    • list(), tuple(), dict()
    • 리스트나 가변형 시퀀스의 경우는 l2 = l1[:] 같이 [:]로도 사본을 생성할 수 있음
    • 이 두 방법은 얕은 사본(shallow copy)
    • 얕은 사본시 모든 항목이 불변형이면 메모리를 절약해 문제를 일으키지 않지만, 가변 항목이 들어가면 문제가 생김
    • pythontutor에서 아래 코드를 실행해보면 시각적으로 볼 수 있음
      l1 = [3, [66, 55, 44], (7, 8, 9)]
      l2 = list(l1)
      l1.append(100)
      l1[1].remove(55)
      print('l1:', l1)
      >>> l1: [3, [66, 44], (7, 8, 9), 100]
      print('l2:', l2)
      >>> l2: [3, [66, 44], (7, 8, 9)]
      l2[1] += [33, 22]
      l2[2] += (10, 11)
      print('l1:', l1)
      >>> l1: [3, [66, 44, 33, 22], (7, 8, 9), 100]
      print('l2:', l2)
      >>> l2: [3, [66, 44, 33, 22], (7, 8, 9, 10, 11)]
    
    • 리스트에서 += 연산자는 리스트를 변경
    • 튜플에서 += 연산자는 새로운 튜플을 만들어 l2[2]에 다시 바인딩
  • 깊은 복사와 얕은 복사
    • deepcopy() : 깊은 복사
      • 객체에 순환 참조가 있으면 무한루프에 빠질 수 있음
      • 너무 깊이 복사하는 경우도 존재 : 복사하면 안되는 외부 리소스나 싱글턴을 객체가 참조하는 경우
    • copy() : 얕은 복사

Reference


카일스쿨 유튜브 채널을 만들었습니다. 데이터 분석, 커리어에 대한 내용을 공유드릴 예정입니다.

PM을 위한 데이터 리터러시 강의를 만들었습니다. 문제 정의, 지표, 실험 설계, 문화 만들기, 로그 설계, 회고 등을 담은 강의입니다

이 글이 도움이 되셨거나 의견이 있으시면 댓글 남겨주셔요.

Buy me a coffeeBuy me a coffee





© 2017. by Seongyun Byeon

Powered by zzsza