Computer >> 컴퓨터 >  >> 프로그래밍 >> Python

파이썬 코드 최적화를 위한 필수 팁 11가지

파이썬은 컴파일 언어에 비해 속도가 느리다는 인식이 널리 퍼져 있지만, 수많은 대형 기업들이 파이썬으로 방대한 워크로드를 안정적으로 처리하고 있습니다. 이는 파이썬 코드 역시 결코 느리지 않으며, 충분히 빠르고 효율적으로 작동할 수 있음을 보여주는 사례입니다. 이 글에서는 올바르게 작성된 파이썬 프로그램을 한층 더 빠르고 효율적으로 만들기 위해 반드시 알아두어야 할 최적화 팁들을 소개합니다.

팁 1: 내장 함수(Built-in Functions) 활용하기

파이썬으로 직접 효율적인 코드를 작성할 수 있지만, 내장 함수(내부적으로 C로 구현됨)보다 빠른 성능을 내기는 매우 어렵습니다. 복잡한 로직을 직접 구현하기보다 파이썬이 제공하는 내장 함수를 우선적으로 활용하는 것이 좋습니다.

파이썬 코드 최적화를 위한 필수 팁 11가지

팁 2: 다중 할당(Multiple Assignment)으로 변수 교환하기

두 변수의 값을 서로 바꿀 때 임시 변수를 사용하는 대신, 파이썬의 다중 할당 문법을 활용하면 더 간결하고 빠릅니다.

>>> # 비효율적인 방법 - 임시 변수 사용
>>> temp = x
>>> x = y
>>> y = temp

>>> # 권장 방법 - 다중 할당 (훨씬 빠름)
>>> x, y = y, x

팁 3: 전역 변수 대신 지역 변수 사용하기

파이썬은 지역 변수를 조회하는 속도가 전역 변수를 조회하는 속도보다 빠릅니다. 따라서 특별한 이유가 없다면 전역 변수 사용을 피하고 지역 변수를 활용하는 것이 성능에 유리합니다.

팁 4: 멤버십 검사에는 'in' 키워드 활용하기

요소의 포함 여부를 확인할 때는 'in' 키워드를 사용하는 것이 좋습니다. 코드가 깔끔해질 뿐만 아니라 실행 속도도 빠릅니다.

for key in sequence:
    print("Hello ", key)

팁 5: 무한 루프에는 'while 1' 사용하기

소켓 리스닝처럼 프로그램에서 무한 루프가 필요한 경우가 있습니다. 'while True'와 동일하게 동작하지만, 'while 1'은 단일 점프 연산으로 처리되어 약간 더 빠릅니다. 참고로 파이썬 3에서는 두 표현의 성능 차이가 거의 없어졌습니다.

>>> while 1:
    # do something, faster with while 1
>>> while True:
    # do something, same operation but slower than the previous one

팁 6: 리스트 컴프리헨션(List Comprehension) 활용하기

파이썬 2.0부터 도입된 리스트 컴프리헨션은 많은 'for' 및 'while' 블록을 대체할 수 있습니다. 인터프리터가 반복 중 예측 가능한 패턴을 감지하도록 최적화되어 있어 일반 반복문보다 훨씬 빠르며, 가독성이 뛰어나고 카운트용 변수 하나를 아낄 수 있다는 장점도 있습니다.

예를 들어 1부터 25 사이의 짝수를 한 줄로 찾으려면 다음과 같이 작성할 수 있습니다.

>>> # 리스트 컴프리헨션 사용 - 권장 방법
>>> print([i for i in range(25) if i%2 == 0])
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24]

# 일반 반복문 사용 - 비교적 비효율적인 방법
i = 0
evens = []

while i < 25:
    if i%2 == 0:
        evens.append(i)
    i += 1

print(evens)

[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24]

팁 7: 제너레이터(Generator)로 필요한 값을 즉시 생성하기

파이썬 제너레이터를 사용하면 메모리를 절약하고 성능을 개선할 수 있습니다. 예를 들어 영상을 스트리밍할 때 전체 스트림을 한 번에 보내는 것이 아니라 바이트 청크(chunk) 단위로 전송할 수 있습니다.

>>> chunkBytes = (1000 * i for i in range(1000))
>>> next(chunkBytes)
0
>>> next(chunkBytes)
1000
>>> next(chunkBytes)
2000
>>> next(chunkBytes)
3000
>>>

팁 8: itertools 모듈 활용하기

itertools 모듈은 순회(iteration)와 조합(combination) 작업에서 매우 유용하고 효율적입니다.

예를 들어 리스트 [1, 2, 3, 4]의 모든 순열(permutation)을 몇 줄의 파이썬 코드로 생성할 수 있습니다.

>>> import itertools
>>> iter1 = itertools.permutations([1, 2, 3, 4])
>>> list(iter1)
[(1, 2, 3, 4), (1, 2, 4, 3), (1, 3, 2, 4), (1, 3, 4, 2), (1, 4, 2, 3), (1, 4, 3, 2), (2, 1, 3, 4), (2, 1, 4, 3), (2, 3, 1, 4), (2, 3, 4, 1), (2, 4, 1, 3), (2, 4, 3, 1), (3, 1, 2, 4), (3, 1, 4, 2), (3, 2, 1, 4), (3, 2, 4, 1), (3, 4, 1, 2), (3, 4, 2, 1), (4, 1, 2, 3), (4, 1, 3, 2), (4, 2, 1, 3), (4, 2, 3, 1), (4, 3, 1, 2), (4, 3, 2, 1)]

팁 9: bisect 모듈로 정렬 상태 유지하기

bisect 모듈은 별도 설치 없이 사용할 수 있는 이진 탐색(binary search) 구현체이자, 정렬된 시퀀스에 빠르게 삽입할 수 있는 도구입니다.

>>> import bisect
>>> bisect.insort(list, element)

이렇게 하면 요소가 정렬된 위치에 자동으로 삽입되므로, 다시 sort()를 호출해 정렬 상태를 유지할 필요가 없습니다. 긴 시퀀스에서 sort()를 반복 호출하는 것은 매우 큰 비용이 들 수 있으므로 이 방법이 특히 유용합니다.

팁 10: 딕셔너리와 셋(Set)으로 멤버십 검사하기

딕셔너리와 셋은 해시 테이블(hash table)로 구현되어 있기 때문에, 특정 요소의 존재 여부를 확인하는 작업이 파이썬에서 매우 빠릅니다. 경우에 따라 조회 속도가 O(1)에 달하기도 합니다.

>>> lst = ['a', 'ab', 'abc'] # 느림 - 리스트로 멤버십 검사
>>> 'abc' in lst
True
>>> mySet = set(['a', 'ab', 'abc']) # 빠름 - 셋 또는 딕셔너리로 멤버십 검사
>>> 'abc' in mySet
True

팁 11: 파이썬 데코레이터로 결과 캐싱하기

파이썬 데코레이터 기호는 '@'입니다. 데코레이터는 추적(tracing), 잠금(locking), 로깅(logging)뿐만 아니라, 함수가 나중에 필요로 하는 결과를 기억하도록 만들어주는 용도(메모이제이션, memoization)로도 활용할 수 있습니다.

>>> from functools import wraps
>>> def memo(f):
    cache = {}
    @wraps(f)
    def wrap(*arg):
        if arg not in cache:
            cache[arg] = f(*arg)
        return cache[arg]
    return wrap

이 데코레이터를 피보나치(Fibonacci) 함수에 적용해 보겠습니다.

>>> @memo
def fib(i):
    if i < 2:
        return 1
    return fib(i-1) + fib(i-2)

핵심 아이디어는 함수를 향상(데코레이트)시켜 이미 계산한 피보나치 항을 기억하도록 하는 것입니다. 계산 결과가 캐시에 이미 저장되어 있다면 다시 계산할 필요가 없으므로, 재귀 호출이 많은 연산에서 실행 속도가 크게 향상됩니다.