Как оптимизировать свой код на Python, даже если вы новичок

МЕНЮ


Главная страница
Поиск
Регистрация на сайте
Помощь проекту
Архив новостей

ТЕМЫ


Новости ИИРазработка ИИВнедрение ИИРабота разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика

Авторизация



RSS


RSS новости


2025-07-18 11:44

разработка по

Думаете, что вы слишком новичок, чтобы оптимизировать Python? Подумайте еще раз. Эти быстрые советы сделают оптимизацию простой и эффективной с самого начала.

Давайте будем честными. Когда вы изучаете Python, вы, вероятно, не думаете о производительности. Вы просто пытаетесь заставить свой код работать! Но вот в чем дело: чтобы сделать свой код на Python быстрее, вам не нужно становиться опытным программистом в одночасье.

1. Замените циклы на списковые включения

 Давайте начнем с того, что вы, вероятно, делаете постоянно: создание новых списков путем преобразования существующих. Большинство новичков прибегают к циклу for, но у 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")

Этот код создает пустой список с именем result, затем перебирает каждое число в нашем списке ввода, возводит его в квадрат и добавляет к списку результатов. Довольно просто, правда?

После оптимизации

Теперь давайте перепишем это с помощью спискового понимания:

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")

Эта единственная строка делает то же самое, что и наш цикл, но она говорит Python «создать список, где каждый элемент является квадратом соответствующего элемента в числах».[num ** 2 for num in numbers]

Выпуск:

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 item в . В чем проблема? Когда вы делаете элемент в , Python должен искать по всему второму списку, пока не найдет элемент. Это медленно!list2list2

После оптимизации

Вот та же логика, но с использованием набора для более быстрого поиска:

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")

Сначала мы преобразуем список в набор. Затем, вместо проверки наличия элемента в , мы проверяем наличие элемента в . Это крошечное изменение делает тестирование членства практически мгновенным.list2set2

Выпуск:

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

Повышение производительности: это может быть в 100 раз быстрее для больших наборов данных.

Почему это работает? Наборы используют хеш-таблицы под капотом. Когда вы проверяете, находится ли элемент в наборе, Python не выполняет поиск по каждому элементу; Он использует хеш для прямого перехода к тому месту, где должен быть элемент. Это все равно, что иметь указатель книги вместо того, чтобы читать каждую страницу, чтобы найти то, что вы хотите.

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")

Функция начинается с общего числа 0, а затем прибавляет каждое число к этому общему числу. Функция начинает с предположения, что первое число является максимальным, а затем сравнивает каждое второе число, чтобы увидеть, больше ли оно.summax

После оптимизации

Вот то же самое с использованием встроенных функций 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 < len(row) - 1:  # If it's not the last item                 result += ","     # Add a comma         result += " "  # Add a newline after each row     return result  # Test data: 1000 rows with 10 columns each test_data = [[f"item_{i}_{j}" for j in range(10)] for i in range(1000)]  start_time = time.time() csv_plus = create_csv_plus(test_data) plus_time = time.time() - start_time print(f"String concatenation time: {plus_time:.4f} seconds")

Этот код строит нашу строку CSV по частям. Для каждой строки он проходит по каждому элементу, преобразует его в строку и добавляет к нашему результату. Он добавляет запятые между элементами и переводы строк между строками.

После оптимизации

Вот тот же код с использованием метода join:

def create_csv_join(data):     # For each row, join the items with commas, then join all rows with newlines     return " ".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)" ".join(...)

Выпуск:

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

Повышение производительности: объединение строк происходит намного быстрее, чем конкатенация для больших строк.

Почему это работает? Когда вы используете += для конкатенации строк, Python каждый раз создает новый строковый объект, поскольку строки являются неизменяемыми. С большими струнами это становится невероятно расточительным. Метод заранее определяет, сколько именно памяти ему нужно, и строит строку один раз.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) и сохраняя все результаты в списке. Проблема в том, что мы храним все 100 000 обработанных значений в памяти одновременно.

После оптимизации

Вот такая же обработка с помощью генератора:

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

Ключевое отличие в том, что вместо . Ключевое слово делает эту функцию генератором — она производит значения по одному, а не создает их все сразу.yieldappendyield

Выпуск:

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

Повышение производительности: генераторы могут использовать «гораздо» меньше памяти для больших наборов данных.

Почему это работает? Генераторы используют отложенную оценку, они вычисляют значения только тогда, когда вы их запрашиваете. Сам объект генератора крошечный; Он просто запоминает, где он находится в вычислениях.

Заключение

 Оптимизация кода на Python не должна быть пугающей. Как мы уже видели, небольшие изменения в подходе к обычным задачам программирования могут привести к значительному увеличению скорости и использования памяти. Ключевым моментом является развитие интуиции для выбора правильного инструмента для каждой работы.

Помните об этих основных принципах: используйте встроенные функции, когда они существуют, выбирайте подходящие структуры данных для вашего сценария использования, избегайте ненужной повторяющейся работы и внимательно следите за тем, как Python обрабатывает память. Включения списков, наборы для тестирования членства, объединение строк, генераторы для больших наборов данных — все это инструменты, которые должны быть в наборе инструментов каждого начинающего программиста Python. Продолжайте учиться, продолжайте программировать! 


Источник: www.kdnuggets.com

Комментарии: