파이썬 성능을 극대화하는 테크닉

개요

파이썬의 성능을 올리는 프로그래밍 팁은 여러 가지가 있습니다.

여기 몇 가지 유용한 팁과 예시를 소개하겠습니다.


1. 리스트 컴프리헨션 사용

리스트 컴프리헨션 (List Comprehension) 은 for 루프를 사용한 리스트 생성보다 더 빠르고 간결합니다.

소규모 배열보다 대규모 배열 생성시 그 차이를 체감할 수 있습니다.

# 일반적인 방법
squares = []
for x in range(10):
    squares.append(x**2)
print(squares)

# 리스트 컴프리헨션 사용
squares = [x**2 for x in range(10)]
print(squares)

결과는 동일합니다.

참고로 "전문가를 위한 파이썬" 에서 저자인 루시아누 하말류는 List Comprehension을 "지능형 리스트" 라고 표현합니다.

지능형리스트나 리스트 컴프리헨션이나 이름이 중요하진 않지만 용어가 친숙하면 두려움없이 다가가는데 도움이 됩니다.


2. 제너레이터 사용

리스트나 튜플(Tuple) 대신 제너레이터(Generator)를 사용하면 메모리 사용을 줄일 수 있습니다.

# 리스트 사용
nums = [x for x in range(1000000)]

# 제너레이터 사용
nums = (x for x in range(1000000))

리스트는 모든 요소를 메모리에 한 번에 저장합니다. 즉 리스트의 크기만큼 메모리가 필요합니다. 

이를 위해 필요한 메모리 공간이 충분히 확보되어야 하며, 큰 리스트를 만들 경우 메모리 사용량이 급격히 증가할 수 있습니다.

반면, 제너레이터는 필요할 때마다 요소를 하나씩 생성합니다. 제너레이터는 전체 데이터를 한 번에 메모리에 올리지 않고, 데이터를 필요로 할 때 생성하여 처리하므로 메모리 사용이 매우 적습니다.

 

3. 내장 함수 사용

파이썬의 내장 함수(Built-in Function)는 C로 구현되어 있어 매우 빠릅니다. 예를 들어, 리스트의 합계를 계산할 때 다음과 같이 진행 할 수 있습니다.

# 직접 구현한 합계 계산
numbers = [1, 2, 3, 4, 5]
total = 0
for num in numbers:
    total += num    
print(total)

# 내장 함수 사용
total = sum(numbers)
print(total)

결과는 다음과 같습니다.


단, 스스로 합을 구하는 코드를 이해하고 작성할 수 있을 때 내장함수 sum()을 사용해야합니다.

기본적인 알고리즘도 이해, 구현하지 못하면서 특정언어의 내장함수에 의존하는 것은 자신의 힘으로 문제를 해결한 것이 아닙니다.

다른 내장함수, 자료형(min, max, sort, queue, stack, BST 등)의 사용도 마찬가지 입니다.

다만, 이해, 구현가능한 알고리즘을 대회출전, 알고리즘 문제풀이 시 직접 작성하는 것은 비효율적이라는 의미입니다.


4. 지역변수를 선호

전역 변수(Global Variable)는 영역 or 함수 밖에서 정의된 변수이고, 로컬 변수(Local Variable)는 영역 or 함수 안에서 정의된 변수입니다. 로컬 변수를 사용하는 것이 더 빠릅니다.

# 전역 변수 사용
global_var = 5

def func():
    result = global_var + 10
    return result

# 로컬 변수 사용
def func():
    local_var = 5
    result = local_var + 10
    return result

지역변수의 사용은 성능적으로도 빠르지만, 메모리 공간활용측면에서도 계속 유지되는 전역변수보다 효율적입니다.


5. 적절한 자료구조 선택

상황에 맞는 적절한 데이터 구조를 선택하는 것이 중요합니다.

예를 들어, 리스트는 순차적인 데이터 저장에 적합하고, 딕셔너리는 빠른 검색에 적합합니다.

# 리스트를 사용한 검색
my_list = [1, 2, 3, 4, 5]
if 3 in my_list:
    print("Found")

# 딕셔너리를 사용한 검색
my_dict = {1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e'}
if 3 in my_dict:
    print("Found")
    

리스트는 순차 탐색을 사용하므로 최악의 경우 모든 요소를 확인해야 합니다. 시간 복잡도는 O(n)입니다.

딕셔너리는 해시 함수를 사용해 직접 접근하므로 검색 시간이 상수 시간 O(1)에 가깝습니다.

따라서 리스트의 크기가 커질수록 검색 시간이 길어집니다. 반면 딕셔너리는 키(Key)를 통해 즉시 접근하므로 크기와 관계없이 빠르게 검색할 수 있습니다. 

 

6. NumPy 활용

NumPy는 대규모 배열과 행렬 연산을 위한 강력한 라이브러리로, 파이썬 리스트보다 훨씬 빠른 성능을 제공합니다.

import numpy as np

# 리스트를 사용한 연산
list_a = [1, 2, 3, 4]
list_b = [5, 6, 7, 8]
result = [a + b for a, b in zip(list_a, list_b)]
print(result)

# NumPy 배열을 사용한 연산
array_a = np.array([1, 2, 3, 4])
array_b = np.array([5, 6, 7, 8])
result = array_a + array_b
print(result)

결과는 다음과 같습니다.

NumPy Array Class가 제공하는 '+' 연산자로 쉽고 빠르게 행렬연산이 가능합니다.

 

이상으로 모든 설명을 마칩니다. 감사합니다.

댓글

이 블로그의 인기 게시물

Qt Designer 설치하기

PyQt5 기반 동영상 플레이어앱 만들기