티스토리 뷰
리스트 자료형? 튜플 자료형?
프로그래밍 언어에 연관성이 존재하는 데이터를 나열하는 방법을 흔히 "배열"이라고 불리는 형태로 많이 사용한다. 컴퓨터 공학에 입문할 때 배우는 대표적인 언어인 C언어에도 이 배열이라는 형태가 있다. C언어에서 배열을 배울 때 아래와 같은 형태를 먼저 배운다.
#include <stdio.h>
int main(void)
{
int a[5] = {1, 2, 3, 4, 5}
return 0;
}
C언어는 배열을 선언할 때 메모리에 해당하는 배열의 크기를 미리 할당한다. 위 코드에서 a 배열의 크기를 5로 지정했기 때문에 메모리의 5칸을 미리 할당 받는 것이다. 위에서는 정수형 자료를 5개 모두 넣었지만, 만약 {1, 2}
만 줬다고 하면 메모리의 나머지 3칸은 쓰레기 값이 들어가있는 상태이다.
즉, 메모리의 크기를 초기에 미리 정하여 선언하는 것이다. 그리고 처음에 정한 배열의 크기를 수정할 수 없다. 이를 변하지 않고 정적이다고 하여 정적 배열이라고 한다. 물론 malloc()
함수를 사용해서 동적 배열을 사용하는 방법도 있으나, 현재 python 관련 게시글이니까 깊게 들어가지는 않겠다. C언어에서 배열에 관하여 처음 배울 때 정적 배열을 배우고 다음에 동적 배열로 넘어가는게 대부분이다. 왜냐하면, 동적 배열을 이해하기 위해서는 포인터와 메모리 개념을 알아야해서 뒤의 목차에서 배우는 경우가 많다.
하지만, 쉽고도 친숙한 언어인 Python 에서는 아주 쉽게 배열을 가지고 놀 수 있다.
리스트 자료형이 바로 C언어의 동적 배열이라고 받아들이면 되고 튜플 자료형이 정적 배열이라고 생각하면 된다. 더 자세하게 표현하자면, 리스트 자료형은 동적 배열이며 가변 객체를 사용한다. 튜플 자료형은 처음에 배열의 크기와 값을 선언하고 이 요소를 바꿀 수 없다. 즉, 불변 객체를 사용한다. 따라서, 파이썬에서 리스트를 선언하고 만들면 원하는대로 요소를 추가/수정/삭제 할 수 있다.
파이썬의 내부를 살펴보면 대부분 C언어로 구현되어 있는데 이를 CPython이라고 한다. Cpython - Github에서 리스트의 내부구조를 살펴보면,
리스트의 크기를 늘리도록 새롭게 할당하는 코드가 있음을 확인하여, 가변적이라는 특징을 파악할 수 있다.
실습을 통해 가변 객체, 불변 객체 메모리 위치 확인해보기
파이썬에서는 모든 것을 객체로 취급한다. 리스트 자료형은 가변 객체, 튜플 자료형은 불변 객체라는 표현을 사용했다.
- 가변 객체는 기존의 메모리 저장 위치에서 위치가 변경되지 않는다.
- 불변 객체는 메모리 위에 있는 값의 내용이 바뀌지 않는다. 따라서, 메모리 저장 위치가 바뀐다.
무슨 말인지 잘 와닿지 않을 것이다. 코드를 통해서 살펴보겠다.
str1 = "hello"
print(str1)
# 출력결과 : hello
str1 += " world!"
print(str1)
# 출력결과 : hello world!
문자열 str은 대표적인 불변 객체이다. str1 변수는 hello라는 값을 가지고 있었는데 거기에 world!를 더해서 str1 변수의 값이 hello world!가 된 것 같이 보인다. 하지만, 메모리의 위치를 확인해보면 사실 hello 값 자체가 바뀐 것은 아니다.
str1 = "hello"
print(id(str1))
# 출력결과 : 140400021790896
str1 += " world!"
print(id(str1))
# 출력결과 : 140400021791152
메모리의 위치가 다른 것을 확인 가능하다. 즉, 값이 변경된 것이 아니라 새로운 문자열을 더하면서 새로운 객체를 형성하여 메모리의 위치가 바뀐 것이다. 불변 객체는 메모리 위에 있는 value를 변경하는 것이 아니다.
list1 = [1, 2, 3]
print(id(list1))
#출력결과 : 140032006714880
list1.append(4)
print(id(list1))
#출력결과 : 140032006714880
대표적인 가변 객체인 리스트를 보면, 리스트에 내용을 추가해서 값을 수정해도 메모리의 위치가 그대로인 것을 확인할 수 있다. 새로운 인덱스가 추가될 때 기존의 메모리 위치에서 메모리를 추가로 할당하여 인덱스가 추가되는 것을 확인할 수 있다.
list1 = [1, 2, 3]
print(id(list1[0]))
print(id(list1[1]))
print(id(list1[2]))
list1.append(4)
print(id(list1[3]))
# 출력결과
# 9788608
# 9788640
# 9788672
# 9788704
리스트는 가변 객체, 튜플은 불변 객체 - 튜플 사용 이유는 ?
가변적으로 편리하게 변하는 배열인 리스트를 사용하면 되는데 굳이 값을 바꿀 수 없는 튜플을 사용하는 이유가 있다.
쉽게 말해서 리스트는 무겁다. 동적 배열인 리스트는 최소한의 공간만큼만 메모리를 할당해준다. 배열이 꽉 찬 상태에서 데이터를 추가할 때는 더블링하여 공간을 확보해준다. 처음에 정적으로 메모리 크기를 고정해준 것과는 다르게, 메모리 크기가 늘어날 수 있기 때문에 불변 객체인 튜플에 비하여 가변 객체인 리스트의 오버헤드 또한 크다.
그래서, 만약 값이 고정되어 있고 그렇게 크지 않은 공간만이 필요한 데이터를 사용한다면 리스트를 사용하는게 낭비일 수 있다. 이런 경우에는 튜플을 사용하는 것이 성능을 향상하는데 더욱 도움이 될 것 이다.
솔직히, 웬만한 상황에서는 리스트를 사용하는 경우가 많겠지만, 프로그래머는 사용자가 더 좋은 성능으로 사용하도록 서비스를 제공해야 하기에 튜플이 성능 향상에 더 뛰어나다면 튜플을 사용해야 한다. 아직 내 수준에서는 리스트가 더 나은지 튜플이 더 나은지 상황을 판단할 능력이 부족해서 대부분 리스트를 사용할 것 같기는 하다. T_T
Reference
- 튜플 사용 이유
※ 본 게시글은 공부하면서 작성한 내용이라, 틀린 점이 있을 수 있습니다. 질문 사항이나 틀린 부분은 댓글을 남겨주시기 바랍니다.
- Dynamic Loading
- flex box
- wsl2 오류
- 프로그래머스 데브코스
- JVM ClassLoader
- Java Compile time
- 백엔드 데브코스
- Runtime Constant Pool
- Java 실행원리
- 클라우드 기반 백엔드 데브코스
- 자바 동적로딩
- string pool
- 컴퓨터 성능 지표
- Hotspot Compiler
- 코딩부트캠프
- JVM 실행원리
- CPU execution time
- JVM
- JVM Runtime Data Areas
- JVM Memory Areas
- flex align
- 프로그래머스
- JVM Execute Engine
- flex vertical align
- flex 수직정렬
- Java 컴파일러 동작 원리
- JIT Compiler
- extension 다운 안됨
- vscode 오류
- 가변객체
- Total
- Today
- Yesterday