리스트의 사용성은 좋지만 세부 요구사항에 따라서 더 나은 자료형도 있습니다. 예를 들어, 실수를 수 천만 개 저장할 때 배열이 효율적입니다.
리스트 안에 숫자만 들어 있다면 배열이 리스트보다 훨씬 더 효율적이고, 배열은 pop(), insert(), extend() 등을 포함해서 가변 시퀀스가 제공하는 모든 연산을 지원하고 빠르게 파일에 저장하고 읽어올 수 있는 frombytes()와 tofile()메서드로 추가 제공한다.
배열을 생성할 때는 배열에 저장되는 각 항목의 C기반 형을 결정하는 문자인 타입 코드를 지정한다.
#커다란 실수 배열의 생성, 저장, 로딩
from array import array
from random import random
floats = array('d', (random() for i in range(10**7))) #제너레이터 표현식
floats[-1]
fp = open('floats.bin', 'wb')
floats.tofile(fp)
fp.close()
floats2 = array('d')
fp = open('floats.bin', 'rb')
floats.fromfile(fp,10**7)
fp.close()
floats2[-1]
floats2 ==floats
객체를 직렬화하는 pickle 모듈도 숫자 데이터를 빠르고 융통성 있게 저장할 수 있다. pickle.dump() 메서드는 실수 배열을 array.tofile() 메서드만큼 빠르게 저장할 뿐만 아니라 복소수, 내포된 컬렉션, 사용자 정의 객체 등 거의 모든 내장 자료형을 처리할 수 있다.
래스터 이미지처럼 이진 데이터를 표현하는 숫자 배열을 위해서 파이썬에서는 bytes와 bytearray 형을 제공한다.
#array 형은 list.sort()처럼 배열을 직접 변경하는 메서드가 없고, 배열을 정렬하려면 sorted()함수를 호출하고 배열을 다시 만든다.
a = array.array(a.typecode, sorted(a))
메모리 뷰 내장 클래스는 공유 메모리 시퀀스형으로서 bytes를 복사하지 않고 배열의 슬라이스를 다룰 수 있게 해준다.
메모리 뷰는 본질적으로 파이썬 자체에 들어 있는 Numpy 배열 구조체를 일반화하는 것이고, 데이터 구조체를 복사하지 않고 메모리를 공유할 수 있게 해준다.
array 모듈과 비슷한 표기법을 사용하는 memoryview.cast() 메서드는 바이트를 이동시키지 않고 C언어의 형변환 연산자처럼 여러 바이트로 된 데이터를 읽거나 쓰는 방식을 바꿀 수 있게 해주고 memoryview.cast()는 또 다른 memoryview 객체를 반환하며 언제나 동일한 메모리를 공유한다.
import array
numbers = array.array('h', [-2,-1,0,1,2])
memv = memoryview(numbers)
len(memv)
memv[0]
memv_oct = memv.cast('B')
memv_oct.tolist()
memv_oct[5] = 4
numbers
Numpy는 숫자뿐만 아니라 사용자 정의 레코드로 저장할 수 있는 다차원 동형 배열 및 행렬을 구현하고 요소 단위에서 효율적으로 연산할 수 있게 해준다.
Scipy는 Numpy를 기반으로 작성된 라이브러리로서, 선형대수학, 수치해석, 통계학에 나오는 여러 과학 계산 알고리즘을 제공한다.
Scipy는 Netlib 리포지토리가 제공하는 C 및 포트란 코드 기반을 활용함으로써 빠르고 신뢰성이 높기에 Scipy는 C와 포트란에서 최적화되고 업계에서 입증된 수치 계산 함수를 대화형 고급 파이썬 API를 제공한다.
import numpy
floats = numpy.loadtxt('floats-10M-lines.txt')
floats[-3:]
floats *= 5
floats[-3:]
from time import perf_counter as pc
t0 = pc(); floats /= 3; pc() -t0
numpy.save('floats-10M', 'r+')
floats2 *=6
floats2[-3:]
$ sudo apt-get install python-numpy python-scipy
append()와 pop() 메서드를 사용해서 리스트를 스덱이나 큐로 사용할 수 있지만 리스트 왼쪽에 삽입하거나 삭제하는 연산은 전체 리스트를 이동시켜야 하므로 처리 부담이 크다.
덱 클래스는 큐의 양쪽 어디에서든 빠르게 삽입 및 삭제할 수 있도록 설계된 스레드 안전한 양방향 큐이고 최대 길이를 설정해서 제한된 항목만 유지할 수도 있으므로 덱이 꽉 찬 후에는 새로운 항목을 추가할 때 반대쪽 항목을 버린다.
from collections import deque
dq = deque(range(10), maxlen=10)
dq
dq.rotate(3)
dq
dq.rotate(-4)
dq
dq.appendleft(-1)
dq
dq.extend([11,22,33])
dq
dq.extendleft([10,20,30,40])
dq
덱은 리스트 메서드 대부분을 구현할 뿐만 아니라 popleft() 와 rotate()처럼 고유한 메서드를 추가로 가지고 있다. 덱의 중간 항목을 삭제하는 연산은 빠르지 않는다.(왜냐하면, 덱이 양쪽 끝에 추가나 제거하는 연산에 최적화되어 있기 때문이다.)
queue
동기화된 Queue, LifoQueue, PriorityQueue 클래스를 제공한다. 주로, 스레드 간에 안정하게 통신하기 위해서 사용이 됩니다. 새 클래스 모두 0보다 큰 maxsize 인수를 생성자에 전달해서 바인딩할 수 있지만 덱과 달리 공간이 꽉 찾을 때 항목을 버리지 않지만 새로운 항목의 추가를 블로킹하고 다른 스레드에서 큐 안의 항목을 제거해서 공간을 확보해줄 때까지 기다리게 활성화된 스레드 수를 조절하기 좋다.
multiprocessing
queue.Queue와 비슷하지만 프로세스 간 통신을 지원하기 위해서 설계된 고유한 Queue 클래스를 구현하고 테스크 관리에서 특화된 multiprocessing.JoinableQueue 클래스도 제공한다.
asyncio
asyncio 모듈은 queue 및 multiprocessing 모듈에 포함된 클래스로부터 영감을 얻은 Queue, LifoQueue, PriorityQueue 클래스를 제공하지만, 비동기 프로그래밍 환경에서 작업을 관리하는데 주안점을 두고 있다.
heapq
앞 서 나온 세 가지 모듈과 대조적으로 queue를 구현하지 않지만, 가변 시퀀스를 힙큐나 우선순위 큐로 사용할 수 있게 해주는 heappush()와 heappop()등의 함수를 제공한다.