파이썬 표준 라이브러리는 C로 구현된 다음과 같은 시퀀스형을 제공한다.

컨테이너 시퀀스

  1. 서로 다른 자료형의 항목들을 담을 수 있는 list, tuple, collections.deque 형
  2. 객체에 대한 참조를 담고 있으며 객체는 어떠한 자료형도 될수 있다

컨테이너 시퀀스 예제 코드

listt 예제

# list는 다양한 데이터 타입을 담을 수 있습니다.
mixed_list = [1, "Python", 3.14, [2, 4, 6]]
print(mixed_list)

[1, 'Python', 3.14, [2, 4, 6]]

tuple 예제

#tuple은 불변의 컨테이너입니다.
fixed_tuple = (1, "Python", 3.14, (2, 4, 6))
print(fixed_tuple)

(1, 'Python', 3.14, (2, 4, 6))

collections.deque 예제

from collections import deque

# deque는 양 끝에서 빠른 삽입과 삭제가 가능합니다.
dq = deque(["Mon", "Tue", "Wed"])
dq.append("Thu")  # 끝에 추가
dq.appendleft("Sun")  # 앞에 추가
print(dq)

deque(['Sun', 'Mon', 'Tue', 'Wed', 'Thu'])

균일 시퀀스

  1. 단 하나의 자료형만 담을 수 있는 str, bytes, bytearray, memoryview, array.array 형
  2. 객체에 대한 참조 대신 자신의 메모리 공간에 각 항목의 값을 직접 담는다 따라서 균일 시퀀스가 메모리를 더 적게 사용하지만, 문자, 바이트, 숫자 등 기본적인 자료형만 저장할 수 있다.

균일 시퀀스 예제 코드

str 예제

# 문자열은 문자의 시퀀스입니다.
greeting = "Hello, Python!"
print(greeting)

Hello, Python!

bytes 예제

# bytes는 불변의 바이트 시퀀스입니다.
immutable_bytes = b'\x00\x01\x02'
print(immutable_bytes)

b'\x00\x01\x02'

array.array 예제

from array import array

# array는 숫자 데이터를 효율적으로 저장합니다.
numbers = array('i', [1, 2, 3, 4, 5])  # 'i'는 정수(integer) 타입 코드입니다.
print(numbers)

array('i', [1, 2, 3, 4, 5])

가변 시퀀스

  1. list, bytearray, array.array, collections.deque, memoryview 형

가변 시퀀스 예제 코드

가변 시퀀스 예제 (list)

# list는 가변 시퀀스입니다.
fruits = ["apple", "banana", "cherry"]
fruits[2] = "orange"  # 항목 변경 가능
print(fruits)

['apple', 'banana', 'orange']

불변 시퀀스

  1. tuple, str, bytes 형

불변 시퀀스 예제 코드

불변 시퀀스 예제 (tuple)

# tuple은 불변 시퀀스입니다.
colors = ("red", "green", "blue")
# colors[1] = "yellow"  # 이 코드는 에러를 발생시킵니다.
print(colors)

('red', 'green', 'blue')

timeit을 사용하여 간단한 리스트와 배열의 성능 차이를 측정

import timeit

# 리스트와 배열의 생성 시간을 비교하기 위한 timeit 설정
list_setup = "import random"
array_setup = "import array; import random"

# 리스트 생성에 대한 timeit 코드
list_time = timeit.timeit("[(random.random()) for _ in range(1000000)]", setup=list_setup, number=10)

# 배열 생성에 대한 timeit 코드
array_time = timeit.timeit(
    'array.array("d", [(random.random()) for _ in range(1000000)])', setup=array_setup, number=10
)

print(f"생성 List time: {list_time}, 생성 Array time: {array_time}", sep="\n")

# 리스트와 배열에서 모든 항목에 대해 반복 연산을 수행하는 시간을 비교하기 위한 timeit 설정
# 미리 생성된 리스트와 배열을 사용하여 반복 연산 수행
list_repeat_setup = """
import random
my_list = [random.random() for _ in range(1000000)]
"""

array_repeat_setup = """
import array
import random
my_array = array.array("d", [random.random() for _ in range(1000000)])
"""

# 리스트의 모든 항목에 대해 반복 연산을 수행하는 timeit 코드
list_repeat_time = timeit.timeit("for item in my_list: pass", setup=list_repeat_setup, number=100)

# 배열의 모든 항목에 대해 반복 연산을 수행하는 timeit 코드
array_repeat_time = timeit.timeit("for item in my_array: pass", setup=array_repeat_setup, number=100)

print(f"순회 List time: {list_repeat_time}, 순회 Array time: {array_repeat_time}", sep="\n")


결과
생성 List time: 0.5007023000071058, 생성 Array time: 0.6433108000055654
순회 List time: 0.48309470000094734, 순회 Array time: 0.7433342000003904

리스트가 배열보다 생성시간 및 순회시간이 더짧은 이유

타입 체크 오버헤드

배열은 특정 타입의 값만을 담기 때문에, 순회 시 타입 체크에 대한 오버헤드가 발생할 수 있습니다. 반면 리스트는 어떠한 타입의 객체도 담을 수 있으며, 내부적으로 객체에 대한 포인터를 저장하므로 타입 체크가 필요하지 않습니다.

함수 호출 오버헤드

파이썬의 배열은 array 모듈에 정의된 array.array 타입입니다. 이는 C 언어로 구현된 내장 모듈이지만, 리스트보다 더 많은 함수 호출을 필요로 할 수 있습니다. 리스트는 파이썬의 가장 기본적인 자료형 중 하나이므로, Python 인터프리터에 의해 매우 최적화되어 있습니다.

메모리 레이아웃

리스트는 객체에 대한 포인터의 배열이고, 객체 자체는 힙 메모리에 저장됩니다. 이는 메모리에서 연속적이지 않을 수도 있으나, 포인터 접근은 매우 빠르게 이루어집니다. 반면에 배열은 모든 데이터를 연속된 메모리 블록에 저장합니다. 이는 일반적으로 더 효율적이지만, 파이썬에서는 추가적인 오버헤드가 발생할 수도 있습니다.

최적화 수준

리스트는 파이썬에서 가장 일반적으로 사용되는 자료형 중 하나이기 때문에, 파이썬 인터프리터와 표준 라이브러리는 리스트의 성능을 최적화하는데 많은 노력을 기울여 왔습니다. 배열은 특정 경우에 최적화되어 있지만, 일반적인 사용 케이스에서 리스트만큼 최적화되지 않았을 수 있습니다.

특정 시나리오에서는 리스트가 배열보다 빠를 수 있음을 설명합니다. 그러나 이러한 성능 차이는 작업의 종류, 데이터의 크기, 파이썬의 버전 및 구현에 따라 달라질 수 있으므로, 항상 성능을 직접 측정하여 확인하는 것이 좋습니다.

profile
백엔드개발자

0개의 댓글