본문 바로가기
공부/Python

[Python] 파이썬 실행 & 메모리 관리 방식, mutable & immutable 객체, 깊은 복사 (deep copy)

by haejang 2022. 12. 4.
728x90
728x90

 

 

# Ref

 

# 컴파일러와 인터프리터


  • 고급 프로그래밍 언어를 어셈블리어로 번역해주는 방식의 차이
  • 컴파일러 (complier)
    • 전체 파일을 스캔하여 한번에 번역 -> 한 번 실행 파일이 만들어지고 나면 빠름
    • 기계어 번역 과정에서 더 많은 메모리 사용
    • 전체 코드를 스캔 후 오류를 출력하기 때문에 실행 전에 오류를 알 수 없음
    • C, C++, JAVA
  • 인터프리터 (interpreter)
    • 한번에 한 문장씩 번역 -> 실행 시간이 느림
    • 메모리 효율이 좋음
    • 실행 중 오류를 만나면 바로 프로그램 중지
    • Python, Ruby, Javascript

 

# 파이썬 실행 방식


  • 파이썬 코드를 bytecode로 변환시키는 컴파일링 + 한 줄 한 줄 읽으며 해석하는 인터프리팅 혼합 사용
  • 컴파일타임 : 파이썬이 실행되어 0과 1로 구성된 바이트코드로 변환되는 시간
  • 런타임 : 변환된 코드를 메모리에 올려 차례차례 실행
  • 런타임 시 bytecode는 python virtual macine이란 가상머신에서 실행 (외부 모듈은 이 때 추가됨)
  • 파이썬 인터프리터의 종류 : cpython (default), pypy, jython ,,

 

# 파이썬 메모리 관리


  • 메모리 할당 방식
    • 정적 메모리 할당
      • 프로그램 컴파일 시 메모리가 할당됨
      • stack 자료구조로 구현
      • 프로그램이 끝날때까지 없어지지 않음 : 메모리 재사용 불가
    • 동적 메모리 할당
      • 프로그램 런타임 시 메모리가 할당됨
      • heap 자료구조로 구현
      • 메모리에서 데이터 제거 가능 : 필요하지 않은 메모리 비우고 재사용 가능
  • 변수 할당
    • 파이썬은 정적변수/동적변수를 구분하지 않으며,
      (인터프리터마다 다르지만) cpython의 경우 전부 heap에 올림
    • 파이썬의 모든 변수는 객체로 간주된다 : 모든 변수는 어떤 메모리 공간으로의 레퍼런스를 가짐
    • cpython에서의 immutable(아래에서 설명)한 변수들에 대한 메모리 관리 예제 (출처)

a = 3 실행 시 레퍼런스(a)와 오브젝트(3)가 스택과 힙에 올라감
b = 5 를 실행해도 마찬가지
a = 5 실행 시, 오브젝트의 값은 변경이 없으며 레퍼런스만 변경됨
c = 5 가 실행되어도 5라는 object는 이미 존재하기 때문에 레퍼런스만 생성됨
파이썬은 자동으로 레퍼런스가 0인 오브젝트 3을 삭제한다

 

# mutable, immutable


  • 위의 예제는 immutable (변경 불가능)한 객체인 int형 객체였다
  • 따라서 메모리 상에서 object는 변하지 않고, reference만 변경되었다 (interning)
  • immutable : int, float, bool, str ,,,
  • mutable : list, dict

  • a, b, c에 각각 int인 1을 넣어줬을 땐, 1이라는 동일한 객체를 바라보고 있으므로 id값들이 모두 같다
  • a, b, c에 각각 list인 [1,2,3] 을 넣어준 경우, mutable한 list type의 변수이므로 각각 다른 id값을 갖는다

  • 따라서 안의 내용이 달라져도 레퍼런스값이 달라지지 않는다
  • 이는 다시말해 레퍼런스값만 알면 오브젝트값을 변화시킬 수 있다는 뜻이 되는데,

  • 단순히 list를 복사하는 경우, 주소값을 복사하기 때문에 (shallow copy) 원본 객체의 데이터를 수정할 수 있게 된다

 

# 얕은 복사 (shallow copy), 깊은 복사 (deep copy)


  • 얕은 복사 : 원본 객체의 주소값을 복사함
  • 깊은 복사 : 원본 객체가 참조하는 객체 자체를 복사
  • 깊은 복사를 하기 위한 방법 (출처)

copy 모듈의 deepcopy() 이용 : 가장 많이 쓰임

import copy

a = [1, 2, 3]
b = copy.deepcopy(a)

 

각 클래스의 copy() 함수 이용

a = [1, 2, 3]
b = a.copy()

그 외,,,

a = [1, 2, 3]

# list 생성 시 매개변수에 원본 전달
b = list(a)

# list 생성 후 원본 리스트로 확장
c = []
c.extend(a)

# list slicing
d = a[:]

# 배열 요소로의 접근을 통한 복사
e = [i for i in a]
f = []
for i in a:
	f.append(i)

 

이런저런 여러 방법들이 있으나, 처리 속도 면에서는 list slicing이 제일 빠르고, deepcopy가 제일 느리다고 한다

 

참조 : https://stackoverflow.com/questions/2612802/how-do-i-clone-a-list-so-that-it-doesnt-change-unexpectedly-after-assignment

 

How do I clone a list so that it doesn't change unexpectedly after assignment?

While using new_list = my_list, any modifications to new_list changes my_list every time. Why is this, and how can I clone or copy the list to prevent it?

stackoverflow.com

 

from copy import deepcopy

dignore = {str: None, int: None, type(None): None}

if __name__ == '__main__':
    import copy
    from time import time

    num_times = 100000
    L = [None, 'blah', 1, 543.4532, 
         ['foo'], ('bar',), {'blah': 'blah'},]
    
    t = time()
    for i in range(num_times):
        copy.copy(L)
    print('copy.copy:              ', time()-t)

    t = time()
    for i in range(num_times):
        copy.deepcopy(L)
    print('copy.deepcopy:          ', time()-t)
    
    t = time()
    for i in range(num_times):
        L.copy()
    print('L.copy():               ', time()-t)
    
    t = time()
    for i in range(num_times):
        list(L)
    print('list(L):                ', time()-t)
    
    t = time()
    for i in range(num_times):
        a = []
        a.extend(L)
    print('a.extend(L):            ', time()-t)

    t = time()
    for i in range(num_times):
        L[:]
    print('L[:]:                   ', time()-t)    

    t = time()
    for i in range(num_times):
        [i for i in L]
    print('[i for i in L]:         ', time()-t)

    t = time()
    for i in range(num_times):
        a = []
        for y in L:
            a.append(y)
    print('for y in L: a.append(y):', time()-t)

 

각 deep copy별 시간 계산하는 것을 좀 더 내가 보기 쉽게 고쳐보았다

그렇다고 한다

 

 

 

 

 

 

 

728x90
728x90

댓글