리스트가 답이 아닐 때

매일 공부(ML)·2022년 11월 7일
0

Fluent Python

목록 보기
11/130

시퀀스

리스트가 답이 아닐 때

리스트의 사용성은 좋지만 세부 요구사항에 따라서 더 나은 자료형도 있습니다. 예를 들어, 실수를 수 천만 개 저장할 때 배열이 효율적입니다.

배열

리스트 안에 숫자만 들어 있다면 배열이 리스트보다 훨씬 더 효율적이고, 배열은 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는 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()등의 함수를 제공한다.

profile
성장을 도울 아카이빙 블로그

0개의 댓글