초보자라도 파이썬 코드를 최적화하는 방법

초보자라도 파이썬 코드를 최적화하는 방법

초보자라도 파이썬 코드를 최적화하는 방법초보자라도 파이썬 코드를 최적화하는 방법
저자의 이미지 | 표의 문자

솔직히합시다. Python을 배우면 성능에 대해 생각하지 않을 것입니다. 당신은 당신의 코드를 작동 시키려고 노력하고 있습니다! 그러나 다음은 다음과 같습니다. 파이썬 코드를 더 빨리 만들면 밤새 전문가 프로그래머가 될 필요는 없습니다.

오늘 보여줄 몇 가지 간단한 기술로 코드의 속도와 메모리 사용량을 크게 향상시킬 수 있습니다.

이 기사에서는 5 가지 실용적인 초보자 친화적 최적화 기술을 함께 걸겠습니다. 각각에 대해, 나는 당신에게 “이전”코드 (많은 초보자가 쓰는 방식), “After”코드 (최적화 버전)를 보여주고 개선이 왜 작동하는지, 얼마나 더 빠른지를 정확하게 설명하겠습니다.

🔗 Github의 코드 링크

1. 루프를 목록 이해로 바꾸십시오

기존 목록을 변환하여 새로운 목록을 만들 수있는 모든 일부터 시작합시다. 대부분의 초보자는 For Loop에 도달하지만 Python 은이 작업을 수행하는 훨씬 빠른 방법을 가지고 있습니다.

최적화 전

대부분의 초보자가 숫자 목록을 제곱하는 방법은 다음과 같습니다.

import time

def square_numbers_loop(numbers):
    result = [] 
    for num in numbers: 
        result.append(num ** 2) 
    return result

# Let's test this with 1000000 numbers to see the performance
test_numbers = list(range(1000000))

start_time = time.time()
squared_loop = square_numbers_loop(test_numbers)
loop_time = time.time() - start_time
print(f"Loop time: {loop_time:.4f} seconds")

이 코드는 결과라는 빈 목록을 생성 한 다음 입력 목록의 각 숫자를 루프하고 제곱 한 다음 결과 목록에 추가합니다. 꽤 간단합니까?

최적화 후

이제 목록 이해력을 사용하여 이것을 다시 작성해 봅시다.

def square_numbers_comprehension(numbers):
    return [num ** 2 for num in numbers]  # Create the entire list in one line

start_time = time.time()
squared_comprehension = square_numbers_comprehension(test_numbers)
comprehension_time = time.time() - start_time
print(f"Comprehension time: {comprehension_time:.4f} seconds")
print(f"Improvement: {loop_time / comprehension_time:.2f}x faster")

이 단일 라인 [num ** 2 for num in numbers] 루프와 똑같은 일을하지만 Python에게 “각 요소가 해당 요소의 정사각형 인 목록을 만듭니다.”

산출:

Loop time: 0.0840 seconds
Comprehension time: 0.0736 seconds
Improvement: 1.14x faster

성능 향상: 목록 이해력은 일반적으로 등가 루프보다 30-50% 빠릅니다. 매우 큰 반복과 함께 일할 때 개선이 더 눈에 띄게됩니다.

이것이 왜 작동합니까? 목록 이해력은 후드 아래 C에서 구현되므로 Python 루프, 가변 조회 및 장면 뒤에서 발생하는 기능 통화와 같은 많은 오버 헤드를 피합니다.

2. 작업에 적합한 데이터 구조를 선택하십시오

이것은 거대하며 작은 변화로 코드를 수백 배 더 빨리 만들 수있는 것입니다. 핵심은 목록 대 세트 대 사전을 사용하는시기를 이해하는 것입니다.

최적화 전

두 목록 사이에 공통 요소를 찾고 싶다고 가정 해 봅시다. 직관적 인 접근법은 다음과 같습니다.

def find_common_elements_list(list1, list2):
    common = []
    for item in list1:  # Go through each item in the first list
        if item in list2:  # Check if it exists in the second list
            common.append(item)  # If yes, add it to our common list
    return common

# Test with reasonably large lists
large_list1 = list(range(10000))     
large_list2 = list(range(5000, 15000))

start_time = time.time()
common_list = find_common_elements_list(large_list1, large_list2)
list_time = time.time() - start_time
print(f"List approach time: {list_time:.4f} seconds")

이 코드는 첫 번째 목록을 통해 반복되며 각 항목에 대해 IF 항목을 사용하여 두 번째 목록에 해당 항목이 있는지 확인합니다. list2. 문제? 항목을 할 때 list2Python은 항목을 찾을 때까지 두 번째 목록 전체를 검색해야합니다. 느린다!

최적화 후

동일한 논리는 다음과 같지만 더 빠른 조회를 위해 세트를 사용합니다.

def find_common_elements_set(list1, list2):
    set2 = set(list2)  # Convert list to a set (one-time cost)
    return [item for item in list1 if item in set2]  # Check membership in set

start_time = time.time()
common_set = find_common_elements_set(large_list1, large_list2)
set_time = time.time() - start_time
print(f"Set approach time: {set_time:.4f} seconds")
print(f"Improvement: {list_time / set_time:.2f}x faster")

먼저 목록을 세트로 변환합니다. 그런 다음 항목이 있는지 확인하는 대신 list2항목이 있는지 확인합니다 set2. 이 작은 변화는 멤버십 테스트를 거의 즉각적으로 만듭니다.

산출:

List approach time: 0.8478 seconds
Set approach time: 0.0010 seconds
Improvement: 863.53x faster

성능 향상: 이것은 대형 데이터 세트의 경우 100 배 빠른 순서 일 수 있습니다.

이것이 왜 작동합니까? 세트는 후드 아래에서 해시 테이블을 사용합니다. 항목이 세트에 있는지 확인하면 파이썬은 모든 요소를 검색하지 않습니다. 해시를 사용하여 항목의 위치로 직접 점프합니다. 그것은 당신이 원하는 것을 찾기 위해 모든 페이지를 읽는 대신 책의 색인을 갖는 것과 같습니다.

3. 가능할 때마다 Python의 내장 기능을 사용하십시오

Python에는 최적화 된 수많은 내장 기능이 제공됩니다. 자신만의 루프 또는 사용자 정의 기능을 작성하기 전에 무언가를 수행하기 전에 Python에 이미 기능이 있는지 확인하십시오.

최적화 전

내장에 대해 몰랐을 때 목록의 합계와 최대 값을 계산하는 방법은 다음과 같습니다.

def calculate_sum_manual(numbers):
    total = 0
    for num in numbers:  
        total += num     
    return total

def find_max_manual(numbers):
    max_val = numbers[0] 
    for num in numbers[1:]: 
        if num > max_val:    
            max_val = num   
    return max_val

test_numbers = list(range(1000000))  

start_time = time.time()
manual_sum = calculate_sum_manual(test_numbers)
manual_max = find_max_manual(test_numbers)
manual_time = time.time() - start_time
print(f"Manual approach time: {manual_time:.4f} seconds")

그만큼 sum 함수는 총 0으로 시작한 다음 각 숫자를 해당 총에 추가합니다. 그만큼 max 기능은 첫 번째 숫자가 최대 값이라고 가정하여 시작한 다음 다른 모든 숫자를 비교하여 더 큰지 확인합니다.

최적화 후

Python의 내장 기능을 사용하는 것과 같은 것은 다음과 같습니다.

start_time = time.time()
builtin_sum = sum(test_numbers)    
builtin_max = max(test_numbers)    
builtin_time = time.time() - start_time
print(f"Built-in approach time: {builtin_time:.4f} seconds")
print(f"Improvement: {manual_time / builtin_time:.2f}x faster")

그게 다야! sum() 목록의 모든 숫자를 제공하고 max() 가장 큰 숫자를 반환합니다. 동일한 결과, 훨씬 더 빠릅니다.

산출:

Manual approach time: 0.0805 seconds
Built-in approach time: 0.0413 seconds
Improvement: 1.95x faster

성능 향상: 내장 기능은 일반적으로 수동 구현보다 빠릅니다.

이것이 왜 작동합니까? Python의 내장 기능은 C로 작성되어 크게 최적화되었습니다.

4. Join으로 효율적인 문자열 작업을 수행하십시오

문자열 연결은 모든 프로그래머가하는 일이지만, 대부분의 초보자는 문자열이 길어질 때 기하 급수적으로 느리게하는 방식으로 수행합니다.

최적화 전

다음은 + 연산자와 연결하여 CSV 문자열을 구축하는 방법입니다.

def create_csv_plus(data):
    result = ""  # Start with an empty string
    for row in data:  # Go through each row of data
        for i, item in enumerate(row):  # Go through each item in the row
            result += str(item)  # Add the item to our result string
            if i 

이 코드는 CSV 문자열을 조각별로 구축합니다. 각 행마다 각 항목을 통해 문자열로 변환하여 결과에 추가합니다. 항목과 줄 사이에 쉼표가 추가됩니다.

최적화 후

조인 메소드를 사용하는 동일한 코드는 다음과 같습니다.

def create_csv_join(data):
    # For each row, join the items with commas, then join all rows with newlines
    return "\n".join(",".join(str(item) for item in row) for row in data)

start_time = time.time()
csv_join = create_csv_join(test_data)
join_time = time.time() - start_time
print(f"Join method time: {join_time:.4f} seconds")
print(f"Improvement: {plus_time / join_time:.2f}x faster")

이 단일 라인은 많은 기능을합니다! 내부 부분 ",".join(str(item) for item in row) 각 행을 가져 와서 쉼표로 모든 항목을 결합합니다. 바깥 부분 "\n".join(...) 쉼표로 구분 된 모든 행을 가져 와서 최신 선과 합류합니다.

산출:

String concatenation time: 0.0043 seconds
Join method time: 0.0022 seconds
Improvement: 1.94x faster

성능 향상: 문자열 결합은 큰 문자열의 연결보다 훨씬 빠릅니다.

이것이 왜 작동합니까? 문자열에 +=를 연결하면 문자열이 불변이기 때문에 파이썬은 매번 새 문자열 객체를 만듭니다. 큰 문자열로 이것은 엄청나게 낭비됩니다. 그만큼 join 메소드는 선불로 필요한 메모리를 정확히 파악하고 문자열을 한 번 빌드합니다.

5. 메모리 효율적인 처리에 생성기를 사용하십시오

때로는 모든 데이터를 메모리에 한 번에 저장할 필요가 없습니다. 생성기를 사용하면 주문형 데이터를 생성 할 수 있으므로 대량의 메모리를 절약 할 수 있습니다.

최적화 전

다음은 모든 것을 목록에 저장하여 대형 데이터 세트를 처리하는 방법입니다.

import sys

def process_large_dataset_list(n):
    processed_data = []  
    for i in range(n):
        # Simulate some data processing
        processed_value = i ** 2 + i * 3 + 42
        processed_data.append(processed_value)  # Store each processed value
    return processed_data

# Test with 100,000 items
n = 100000
list_result = process_large_dataset_list(n)
list_memory = sys.getsizeof(list_result)
print(f"List memory usage: {list_memory:,} bytes")

이 함수는 0에서 N-1까지의 숫자를 처리하고 각각에 약간의 계산을 적용하고 (제곱, 3을 곱하고, 42를 추가) 모든 결과를 목록에 저장합니다. 문제는 10 만 개의 처리 된 값을 모두 메모리에 한 번에 보관한다는 것입니다.

최적화 후

발전기를 사용한 동일한 처리는 다음과 같습니다.

def process_large_dataset_generator(n):
    for i in range(n):
        # Simulate some data processing
        processed_value = i ** 2 + i * 3 + 42
        yield processed_value  # Yield each value instead of storing it

# Create the generator (this doesn't process anything yet!)
gen_result = process_large_dataset_generator(n)
gen_memory = sys.getsizeof(gen_result)
print(f"Generator memory usage: {gen_memory:,} bytes")
print(f"Memory improvement: {list_memory / gen_memory:.0f}x less memory")

# Now we can process items one at a time
total = 0
for value in process_large_dataset_generator(n):
    total += value
    # Each value is processed on-demand and can be garbage collected

주요 차이점은 yield 대신 append. 그만큼 yield 키워드는 이것을 생성기 함수로 만듭니다. 한 번에 한 번에 한 번에 하나씩 값을 생성합니다.

산출:

List memory usage: 800,984 bytes
Generator memory usage: 224 bytes
Memory improvement: 3576x less memory

성능 향상: 발전기는 대형 데이터 세트에 “많은”메모리를 사용할 수 있습니다.

이것이 왜 작동합니까? 발전기는 게으른 평가를 사용하며 값을 요청할 때만 계산합니다. 생성기 객체 자체는 작습니다. 그것은 계산의 위치를 기억합니다.

결론

파이썬 코드 최적화가 위협적 일 필요는 없습니다. 우리가 보았 듯이, 일반적인 프로그래밍 작업에 접근하는 방법의 작은 변화는 속도와 메모리 사용 모두에서 극적인 개선을 가져올 수 있습니다. 열쇠는 각 작업에 적합한 도구를 선택하기위한 직관을 개발하는 것입니다.

이러한 핵심 원칙을 기억하십시오. 내장 기능이 존재할 때 내장 기능을 사용하고, 사용 사례에 적합한 데이터 구조를 선택하고, 불필요한 반복 작업을 피하고, Python이 메모리를 처리하는 방법을 염두에 두십시오. 목록 이해력, 멤버십 테스트 세트, 문자열 결합, 대형 데이터 세트의 생성기는 모든 초보자 Python 프로그래머의 툴킷에 있어야하는 도구입니다. 계속 배우고 코딩을 계속하십시오!

발라 프리 야 c 인도의 개발자이자 기술 작가입니다. 그녀는 수학, 프로그래밍, 데이터 과학 및 컨텐츠 제작의 교차점에서 일하는 것을 좋아합니다. 그녀의 관심 분야와 전문 지식에는 DevOps, 데이터 과학 및 자연어 처리가 포함됩니다. 그녀는 독서, 쓰기, 코딩 및 커피를 즐깁니다! 현재 그녀는 자습서, 방법 안내, 의견 조각 등을 통해 개발자 커뮤니티와 지식을 배우고 공유하는 작업을하고 있습니다. Bala는 또한 매력적인 리소스 개요 및 코딩 자습서를 만듭니다.

출처 참조

Post Comment

당신은 놓쳤을 수도 있습니다