Как ускорить код на Python в тысячу раз
В любых соревнованиях по скорости выполнения программ Python обычно занимает последние места. Кто-то говорит, что это из-за того, что Python является интерпретируемым языком. Все интерпретируемые языки медленные. Но мы знаем, что Java тоже язык такого типа, её байткод интерпретируется JVM. Как показано, в этом бенчмарке, Java намного быстрее, чем Python.
Вот пример, способный показать медленность Python. Используем традиционный цикл for для получения обратных величин:
import numpy as np np.random.seed(0) values = np.random.randint(1, 100, size=1000000) def get_reciprocal(values): output = np.empty(len(values)) for i in range(len(values)): output[i] = 1.0/values[i] %timeit get_reciprocal(values)
3,37 с ± 582 мс на цикл (среднее значение ± стандартное отклонение после 7 прогонов по 1 циклу)
Ничего себе, на вычисление всего 1 000 000 обратных величин требуется 3,37 с. Та же логика на C выполняется за считанные мгновения: 9 мс; C# требуется 19 мс; Nodejs требуется 26 мс; Java требуется 5 мс(!), а Python требуется аж целых 3,37 СЕКУНДЫ. (Весь код тестов приведён в конце).
Первопричина такой медленности
Обычно мы называем Python языком программирования с динамической типизацией. В программе на Python всё представляет собой объекты; иными словами, каждый раз, когда код на Python обрабатывает данные, ему нужно распаковывать обёртку объекта. Внутри цикла for каждой итерации требуется распаковывать объекты, проверять тип и вычислять обратную величину. Все эти 3 секунды тратятся на проверку типов.
В отличие от традиционных языков наподобие C, где доступ к данным осуществляется напрямую, в Python множество тактов ЦП используется для проверки типа.

Даже простое присвоение числового значения — это долгий процесс.
a = 1
Шаг 1. Задаём a->PyObject_HEAD->typecode тип integer
Шаг 2. Присваиваем a->val =1
Подробнее о том, почему Python медленный, стоит прочитать в чудесной статье Джейка: Why Python is Slow: Looking Under the Hood
Итак, существует ли способ, позволяющий обойти проверку типов, а значит, и повысить производительность?
Решение: универсальные функции NumPy
В отличие list языка Python, массив NumPy — это объект, созданный на основе массива C. Доступ к элементу в NumPy не требует шагов для проверки типов. Это даёт нам намёк на решение, а именно на Universal Functions (универсальные функции) NumPy, или UFunc.

Если вкратце, благодаря UFunc мы можем проделывать арифметические операции непосредственно с целым массивом. Перепишем первый медленный пример на Python в версию на UFunc, она будет выглядеть так:
import numpy as np np.random.seed(0) values = np.random.randint(1, 100, size=1000000) %timeit result = 1.0/values
Это преобразование не только повышает скорость, но и укорачивает код. Отгадаете, сколько теперь времени занимает его выполнение? 2,7 мс — быстрее, чем все упомянутые выше языки:
2,71 мс ± 50,8 мкс на цикл (среднее значение ± стандартное отклонение после =7 прогонов по 100 циклов каждый)
Вернёмся к коду: самое важное здесь — это 1.0/values . values — это не число, а массив NumPy. Наряду с оператором деления есть множество других.

Здесь можно найти все операторы Ufunc.
Подводим итог
Если вы пользуетесь Python, то высока вероятность того, что вы работаете с данными и числами. Эти данные можно хранить в NumPy или DataFrame библиотеки Pandas, поскольку DataFrame реализован на основе NumPy. То есть с ним тоже работает Ufunc.
UFunc позволяет нам выполнять в Python повторяющиеся операции быстрее на порядки величин. Самый медленный Python может быть даже быстрее языка C. И это здорово.
Приложение — код тестов на C, C#, Java и NodeJS
#include #include #include int main() < struct timeval stop, start; int length = 1000000; int rand_array[length]; float output_array[length]; for(int i = 0; igettimeofday(&start, NULL); for(int i = 0; i gettimeofday(&stop, NULL); printf("took %lu us\n", (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec); printf("done\n"); return 0; >
using System; namespace speed_test < class Program< static void Main(string[] args)< int length = 1000000; double[] rand_array =new double[length]; double[] output = new double[length]; var rand = new Random(); for(int i =0; ilong start = DateTimeOffset.Now.ToUnixTimeMilliseconds(); for(int i =0;i long end = DateTimeOffset.Now.ToUnixTimeMilliseconds(); Console.WriteLine(end - start); > > >
import java.util.Random; public class speed_test < public static void main(String[] args)< int length = 1000000; long[] rand_array = new long[length]; double[] output = new double[length]; Random rand = new Random (); for(int i =0; ilong start = System.currentTimeMillis(); for(int i = 0;i long end = System.currentTimeMillis(); System.out.println(end - start); > >
let length = 1000000; let rand_array = []; let output = []; for(var i=0;i let start = (new Date()).getMilliseconds(); for(var i=0;i let end = (new Date()).getMilliseconds(); console.log(end - start);
На правах рекламы
Воплощайте любые идеи и проекты с помощью наших VDS с мгновенной активацией на Linux или Windows. Создавайте собственный конфиг в течение минуты!

- Python
- скорость выполнения
Как ускорить выполнение программы на Python и сократить время работы?
Ниже приведены примеры кода, которые могут помочь в оптимизации производительности:
# Использование словарей вместо списков для поиска элементов data = if "key1" in data: print(data["key1"]) # Использование генераторов списков вместо циклов с добавлением элементов numbers = [1, 2, 3, 4, 5] squared_numbers = [x**2 for x in numbers] # Оптимизация операций с памятью при работе с большими данными # Вместо создания нового списка, используем генератор списков large_data = [x for x in range(1000000)] print(sum(large_data)) # Использование асинхронности для параллельного выполнения задач import asyncio async def my_task(): # Асинхронная задача pass async def main(): tasks = [my_task() for _ in range(10)] await asyncio.gather(*tasks) # Запуск асинхронной программы asyncio.run(main())
Детальный ответ
Как сократить время работы программы Python
Программирование на Python — это увлекательное занятие, но иногда ваша программа может выполняться медленно. Это может быть вызвано множеством факторов, от неэффективного кода до неправильной оптимизации алгоритмов. В этой статье мы рассмотрим некоторые методы, которые помогут вам сократить время работы программы Python.
1. Используйте правильные структуры данных
Один из способов сократить время работы программы — использовать правильные структуры данных. Некоторые структуры данных, такие как списки и кортежи, могут иметь разные характеристики производительности. Например, если вам нужно выполнить поиск элемента в большом списке, использование множества или словаря может быть более эффективным.
# Пример использования словаря для более эффективного поиска data = fruit = 'banana' if fruit in data: print(data[fruit])
2. Правильно оптимизируйте циклы
Циклы могут быть одной из наиболее ресурсоемких операций в программе. Поэтому, чтобы сократить время работы программы, важно оптимизировать циклы. Используйте генераторы списков или генераторы вместо обычных циклов, чтобы избежать создания временных списков или лишних расчетов.
# Пример использования генератора списка numbers = [1, 2, 3, 4, 5] squared_numbers = [x * x for x in numbers] print(squared_numbers)
3. Правильно используйте модули и библиотеки
Python имеет огромное количество сторонних модулей и библиотек, которые могут значительно ускорить выполнение вашей программы. Используйте стандартные библиотеки Python или сторонние модули, чтобы избежать написания кода, который уже реализован в этих библиотеках.
# Пример использования модуля math для выполнения математических операций import math result = math.sqrt(25) print(result)
4. Проверяйте производительность своего кода
Еще один способ сократить время работы программы — проверить производительность вашего кода и найти места, где он замедляется. Для этого вы можете использовать профилировщики, такие как модуль cProfile или сторонние инструменты, такие как line_profiler или memory_profiler.
# Пример использования модуля cProfile для профилирования кода import cProfile def my_function(): # ваш код cProfile.run('my_function()')
5. Переоптимизируйте только при необходимости
Наконец, помните, что оптимизация — это процесс, который может забирать много времени и ресурсов. Не стоит переоптимизировать код, который уже работает достаточно быстро. Оптимизируйте только при необходимости или если вы обнаружили узкие места в производительности программы.
В идеале, когда программа работает правильно и легко читается, вы должны оставить оптимизацию на последний шаг разработки.
Как ускорить ваш код на Python
Python — это универсальный язык программирования, который широко используется в различных областях, таких как веб-разработка, анализ данных, машинное обучение и искусственный интеллект.
Одной из основных причин его популярности является простота и удобочитаемость, что облегчает разработчикам написание и понимание кода. Однако одним из недостатков Python является его производительность, которая относительно ниже по сравнению с другими языками программирования, такими как C или Java. Поэтому очень важно писать эффективный код для обеспечения оптимальной производительности.
В этой статье мы рассмотрим некоторые советы и рекомендации о том, как писать более качественный и эффективный код на Python. Эти методы могут помочь вам оптимизировать код, сократить время его выполнения и улучшить его общее состояние. Давайте начинать!
1. Использование понимания списков
Понимание списков — это краткий и элегантный способ создания списков в Python. Это сокращенный способ создания списка путем указания элементов, которые он должен содержать, и часто он быстрее и эффективнее, чем использование цикла for для создания списка. Например, рассмотрим следующий код:
squares = [] for i in range(10): squares.append(i**2)
Этот код создает список квадратов чисел от 0 до 9, используя цикл for. Мы можем переписать его, используя понимание списка, следующим образом:
squares = [i**2 for i in range(10)]
Этот код более лаконичен и легче читается, и он делает то же самое, что и предыдущий. Более того, понимание списка происходит быстрее, чем использование цикла for, особенно для больших списков.
2. Использование генераторов вместо списков
В Python генератор — это тип итератора, который генерирует последовательность значений на лету. Генераторы более эффективно используют память, чем списки, потому что они не сохраняют все значения в памяти сразу. Вместо этого они генерируют значения одно за другим по мере необходимости, что может значительно сократить объем памяти, занимаемый вашим кодом. Например, рассмотрим следующий код:
numbers = [1, 2, 3, 4, 5] def square_numbers(numbers): result = [] for number in numbers: result.append(number**2) return result squared_numbers = square_numbers(numbers)
Этот код создает список квадратов чисел в списке numbers, используя цикл for. Мы можем переписать этот код с помощью генератора следующим образом:
numbers = [1, 2, 3, 4, 5] def square_numbers(numbers): for number in numbers: yield number**2 squared_numbers = square_numbers(numbers)
Этот код использует генератор вместо списка для генерации квадратов чисел в списке numbers. Ключевое слово yield используется для генерации каждого значения на лету, что может уменьшить объем памяти, занимаемой кодом. Более того, генераторы часто работают быстрее, чем списки для больших наборов данных.
3. Использование функции enumerate()
Функция enumerate()— это встроенная функция Python, которая позволяет перебирать последовательность (например, список или кортеж) и отслеживать индекс текущего элемента. Функция возвращает кортеж вида (index, value), где index— индекс текущего элемента, а value— значение текущего элемента. Эта функция может быть очень полезна, когда вам нужно перебрать последовательность, а также отслеживать индекс текущего элемента. Например, рассмотрим следующий код:
fruits = [‘apple’, ‘banana’, ‘cherry’, ‘date’] for i in range(len(fruits)): print(i, fruits[i])
Этот код выполняет итерацию по списку fruits и выводит индекс и значение каждого элемента в списке. Мы можем переписать этот код с помощью функции enumerate() следующим образом:
fruits = [‘apple’, ‘banana’, ‘cherry’, ‘date’] for i, fruit in enumerate(fruits): print(i, fruit)
Этот код использует функцию enumerate() для перебора списка fruits и отслеживания индекса и значения каждого элемента. Он более лаконичен и легче читается, чем предыдущий код, но делает то же самое.
4. Использование функции zip()
Функция zip()— это еще одна встроенная функция Python, которая позволяет вам параллельно перебирать несколько последовательностей (таких как списки или кортежи). Функция возвращает кортеж вида (value1, value2, . ), где value1— значение текущего элемента в первой последовательности, value2— значение текущего элемента во второй последовательности и так далее. Эта функция может быть очень полезна, когда вам нужно параллельно перебирать несколько последовательностей и обрабатывать их значения вместе. Рассмотрим следующий код:
names = [‘Alice’, ‘Bob’, ‘Charlie’] ages = [25, 30, 35] for i in range(len(names)): print(names[i], ages[i])
Этот код перебирает списки names и ages, перечисляет их элементы и выводит имя и возраст каждого человека. Мы можем переписать этот код с помощью функции zip() следующим образом:
names = [‘Alice’, ‘Bob’, ‘Charlie’] ages = [25, 30, 35] for name, age in zip(names, ages): print(name, age)
В этом коде используется функцию zip() для параллельного перебора списков names и ages и вывода их значений вместе. Этот код более лаконичен и легче читается, чем предыдущий, но происходит в нём то же самое.
5. Использование оператора in для проверки членства
Оператор in— это встроенный оператор Python, позволяющий проверить, является ли элемент членом последовательности (например, списка или кортежа). Оператор возвращает значение True, если элемент находится в последовательности и False в противном случае. Этот оператор может быть очень полезен, когда вам нужно проверить, находится ли элемент в последовательности перед его обработкой. Например, рассмотрим следующий код:
numbers = [1, 2, 3, 4, 5] if len(numbers) > 0: first_number = numbers[0] else: first_number = None
Этот код проверяет, не пуст ли список numbers, и присваивает переменной first_number значение первого элемента. Мы можем переписать этот код с помощью оператора in следующим образом:
numbers = [1, 2, 3, 4, 5] if numbers: first_number = numbers[0] else: first_number = None
В этом коде используется оператор in, чтобы проверить, не пуст ли список numbers перед его обработкой. Он более лаконичен и легче читается, чем предыдущий код, но делает то же самое.
6. Использование аргументов функций вместо глобальных переменных
В Python обычно рекомендуется использовать аргументы функции вместо глобальных переменных, когда это возможно. Глобальные переменные — это переменные, которые определены вне какой-либо функции и доступны из любой части кода. Аргументы функции — это переменные, которые передаются функции и доступны только внутри функции. Использование аргументов функций вместо глобальных переменных может сделать ваш код более модульным, а также упростить его тестирование и поддержку.
Рассмотрим следующий пример:
total = 0 def add_numbers(numbers): global total for number in numbers: total += number return total result = add_numbers([1, 2, 3, 4, 5]) print(result)
Этот код определяет глобальную переменную total и функцию add_numbers, которая добавляет к переменной значения списка чисел total. Затем функция возвращает значение переменной total. Этот код работает, но у него есть несколько проблем. Во-первых, он использует глобальную переменную, что может быть проблематично в больших программах. Во-вторых, он изменяет глобальную переменную внутри функции, что может затруднить анализ поведения кода.
Мы можем переписать этот код, используя аргументы функции, следующим образом:
def add_numbers(numbers): total = 0 for number in numbers: total += number return total result = add_numbers([1, 2, 3, 4, 5]) print(result)
Этот код определяет функцию add_numbers, которая принимает список чисел в качестве аргумента и возвращает сумму чисел. Этот код более модульный, его легче тестировать и поддерживать, чем предыдущий.
7. Использование списковых включений
Списковые включения — это краткий способ создания новых списков на основе существующих. Они позволяют вам написать одну строку кода, которая может заменить несколько строк. Синтаксис списковых включений следующий:
new_list = [expression for item in iterable if condition]
где expression— выражение, которое оценивается для каждого элемента в iterable, item— текущий элемент в iterable, и condition— необязательное условие, фильтрующее элементы, включенные в новый список. Рассмотрим следующий код:
numbers = [1, 2, 3, 4, 5] squares = [] for number in numbers: square = number ** 2 squares.append(square) print(squares)
Этот код создает список квадратов чисел в списке numbers. Мы можем переписать этот код следующим образом:
numbers = [1, 2, 3, 4, 5] squares = [number ** 2 for number in numbers]
Этот код более лаконичен и легче читается, чем предыдущий код, и он делает то же самое.
8. Использование выражений-генераторов
Выражения-генераторы похожи на генераторы списков, но вместо списков они создают генераторы. Генераторы — это объекты, которые генерируют значения на лету и более эффективно используют память, чем списки. Синтаксис выражения генератора такой же, как у генератора списка, но вместо квадратных скобок используются круглые. Например, рассмотрим следующий код:
numbers = [1, 2, 3, 4, 5] squares = (number ** 2 for number in numbers) for square in squares: print(square)
Этот код создает генератор, который генерирует квадраты чисел в списке numbers и перебирает генератор для вывода квадратов. Мы можем переписать этот код следующим образом:
numbers = [1, 2, 3, 4, 5] squares = [number ** 2 for number in numbers] for square in squares: print(square)
Этот код использует понимание списка для создания списка квадратов чисел в списке numbers, а затем выполняет итерацию по списку для вывода квадратов. Этот код работает, но он создает список в памяти, что может быть неэффективно для больших наборов данных. Мы можем переписать этот код, используя выражение-генератор, следующим образом:
numbers = [1, 2, 3, 4, 5] squares = (number ** 2 for number in numbers) for square in squares: print(square)
Этот код создает генератор, который генерирует квадраты чисел в списке numbers и перебирает генератор для в вывода квадратов. Этот код более эффективно использует память, чем предыдущий, и делает то же самое.
9. Использование функции map
Функция map— это встроенная в Python функция, которая применяет функцию к каждому элементу итерируемого объекта и возвращает новый итерируемый объект с результатами. Синтаксис функции map следующий:
map(function, iterable)
где function— функция, применяемая к каждому элементу массива iterable, и iterable— итерация, к которой применяется функция. Например, рассмотрим следующий код:
numbers = [1, 2, 3, 4, 5] def square(number): return number ** 2 squares = map(square, numbers) for square in squares: print(square)
Этот код использует функцию map для применения функции square к каждому числу в списке numbers и создания новой итерации квадратов. Этот код более лаконичен и легче читается, чем предыдущий.
10. Использование функции filter
Функция filter— это встроенная в Python функция, которая фильтрует итерируемый объект на основе функции, возвращающей логическое значение. Синтаксис функции filter следующий:
filter(function, iterable)
где function — функция , которая фильтрует iterable, а iterable является итератором для фильтрации. Например, рассмотрим следующий код:
numbers = [1, 2, 3, 4, 5] def is_even(number): return number % 2 == 0 evens = filter(is_even, numbers) for even in evens: print(even)
Этот код использует функцию filter для фильтрации списка numbers на основе функции is_even и создания новой итерации четных чисел. Этот код более лаконичен и легче читается, чем предыдущий код, но делает он то же самое.
Заключение
В этой статье мы обсудили некоторые советы и рекомендации по написанию лучшего и более эффективного кода Python. Эти советы и рекомендации включают в себя использование функции enumerate, функции zip и других встроенных функций, таких как понимание списков, выражения-генератора, map и filter. Используя эти методы, вы можете писать более краткий, модульный и читаемый код, который также является более эффективным и масштабируемым.
Однако написание эффективного кода Python — это не только использование правильных методов. Это также требует хороших методов программирования, таких как написание ясного и лаконичного кода, использование осмысленных имен переменных и функций, комментирование кода и регулярное его тестирование. Следуя этим практикам и используя методы, которые мы обсуждали в этой статье, вы можете стать более эффективным и действенным программистом на Python.
Общие принципы оптимизации кода Python
«Если необходимо, чтобы Ваш код работал быстрее,то вероятно, следует просто использовать PyPy»
Гвидо ван Россум (создатель Python).
Любой код, в первую очередь, должен быть написан обдуманно и быть рабочим и только потом можно приступать к его дополнительной оптимизации. При этом оптимизация должна проходит прямо в процессе разработки, так как потом сложно будет изменить множество мелких моментов.
Оптимизация производительности в любом языке программирования не имеет четкого алгоритма действий. Иногда даже незначительные изменения в «нужном месте» могут ускорить работу кода Python в несколько раз. Оптимизация кода — это придание важности мелочам.
Избегайте применение глобальных переменных.
Python очень медленно обрабатывает доступ к глобальным переменным, особенно внутри циклов. Также множественное применение глобальных переменных очень часто приводит к спагетти-коду, что вызывает дополнительные трудности и проблемы.
Использование сторонних модулей и библиотек.
Обращайте внимание на каком языке написан сторонний модуль или библиотека. Если есть возможность, то используйте библиотеки, написанные на С, так как они работают быстрее, а это значит, что и программа на Python будет работать немного быстрее.
Активно используйте встроенные инструменты Python.
Их применение ускоряет код за счет того, что они предварительно оптимизированы, скомпилированы и следовательно выполняются быстрее.
Примером таких инструментов может быть:
- встроенная функция map() ;
- встроенная функция str.join() ;
- встроенный модуль itertools ;
- и многое другое.
Работа над кодом.
- пишите код обдуманно и максимально лаконично;
- по возможности, внедряйте кэширование объектов;
- не создавать лишние экземпляры объектов (помните, объекты потребляют дополнительную память);
Перевод статьи словацкого разработчика Мартина Хайнца, в которой он описывает девять практических советов о том, как сделать разработку на Python лучше.
Обрабатывайте входные данные.
Чем больше размер программы, тем выше шансы пропустить уязвимость в коде. Один из способов обезопасить себя от возможных ошибок — очистка входных данных перед выполнением программы (input sanitization). В большинстве случаев при таком подходе достаточно поменять регистр символов или использовать регулярные выражения. Но для сложных случаев есть и более эффективный способ:
user_input = "This\nstring has\tsome whitespaces. \r\n" character_map = ord('\n') : ' ', ord('\t') : ' ', ord('\r') : None > user_input.translate(character_map)
Это пример заменяет «пробельные» символы \n и \t обычным пробелом и удаляет \r (все перечисленные конструкции обозначают разные виды пробелов). В зависимости от задач, можно генерировать таблицы соответствий разного размера. Задачу облегчает встроенный модуль unicodedata и функция combining() для генерации и отображения. Их можно использовать для удаления всех акцентов из строки.
Используйте итераторы со срезами.
Итератор — это инструмент для поточной обработки данных. Он отвечает за упрощение навигации по элементам: списку, словарю и так далее. Это такой объект-перечислитель, который выдаёт следующий элемент. В основном его используют в цикле for/in .
Но использовать итератор на полную мощность нужно не всегда. И тут незадача: если попытаться использовать срез итератора, то получим ошибку TypeError . Это произойдёт из-за того, что объект итератора не является подписываемым. К счастью, на такой случай есть простое решение:
import itertools s = itertools.islice(range(50), 10, 20) for val in s: .
Используя itertools.islice() , можно создать объект islice , который сам по себе является итератором, производящим нужные значения. Важно отметить, что этот объект использует все элементы генератора вплоть до начала среза, что делает itertools.islice() мощным инструментом.
Пропускайте начало итерируемого объекта.
Иногда приходится работать с файлами, которые начинаются с неизвестного количества бесполезных строк, например, с комментариев. И тут модуль itertools снова предлагает простое решение:
string_from_file = """ // Автор: . // Лицензия: . // // Дата: . """ import itertools for line in itertools.dropwhile(lambda line: line.strip().startswith('//'), string_from_file.splitlines()): print(line)
Этот фрагмент кода создаёт строки, пропуская начальные комментарии. Такой подход может быть полезен, если нужно отбросить элементы (в нашем случае строки) в начале итерируемого объекта.
Используйте kwargs .
Если при разработке программы нужно выполнить несколько похожих действий, то лучшее решение — определить функции для многократного использования в коде. Для этого создается функция с аргументами. Но что делать, если аргументы функции определены, а для ее многократного использования (унификации) нужно передавать разное количество аргументов? Для этого можно использовать ключевые аргументы функции kwargs .
Этот инструмент очень пригодится для создания функции только с именованными аргументами. Это даст возможность (или, скорее, обяжет) использовать такие функции более прозрачно:
def test(*, a, b): pass test("value for a", "value for b") # Traceback (most recent call last): # File "ad.py", line 4, in # test("value for a", "value for b") # TypeError: test() takes 0 positional arguments but 2 were given test(a="value1", b="value2") # Работает.
Как видно из примера, задачу легко решить, если поместить аргумент * перед ключевыми словами. И, конечно, можно использовать позиционные аргументы, если вставить их до аргумента * .
Используйте объекты, которые поддерживают оператор with .
Открыть файл и заблокировать фрагмент кода можно с помощью оператора with , но можно ли сделать это, пользуясь собственным методом? Да, можно реализовать протокол context manager, используя методы __enter__ и __exit__ :
class Connection: def __init__(self): . def __enter__(self): # инициализация соединения def __exit__(self, type, value, traceback): # закрытие соединения # использование with Connection() as conn: # __enter__() executes . # conn.__exit__() executes
Это распространённый вариант управления контекстом в Python, но есть и более простой способ:
from contextlib import contextmanager @contextmanager def tag(name): print (f"name>>") yield print (f"name>>") with tag("h1"): print("This is Title.")
Этот фрагмент кода реализует протокол управления контекстом, используя декоратор менеджера contextmanager . Первая часть функции tag() (до yield ) выполняется при входе в блок with , затем исполняется блок, а после него и остальная часть функции tag() .
Сохраните всё с помощью __slots__ .
Если программа создаёт большое количество инстансов какого-либо класса, то она может потребовать больше памяти. Это связано с тем, что Python использует словари для представления атрибутов инстансов классов. Это делает язык быстрым, но не очень эффективным с точки зрения оптимизации памяти. Если это становится проблемой, то поможет магический атрибут __slots__ :
class Person: __slots__ = ["first_name", "last_name", "phone"] def __init__ (self, first_name, last_name, phone): self.first_name = first_name self.last_name = last_name self.phone = phone
При определении атрибута __slots__ Python использует небольшой массив фиксированного размера для атрибутов вместо словаря. Это значительно сокращает объём памяти, необходимый для каждого инстанса.
Следует учесть, что в этом случае есть и недостатки: нельзя объявлять какие-либо новые атрибуты помимо используемых в __slots__ , а классы со __slots__ не могут использовать множественное наследование.
Ограничьте использование процессора и памяти.
Если лень оптимизировать память программы или корректировать работу процессора, то можно просто установить лимиты. К счастью, в Python для этого есть специальный модуль resource :
import signal import resource def time_exceeded(signo, frame): print("CPU exceeded. ") raise SystemExit(1) def set_max_runtime(seconds): soft, hard = resource.getrlimit(resource.RLIMIT_CPU) resource.setrlimit(resource.RLIMIT_CPU, (seconds, hard) ) signal.signal(signal.SIGXCPU, time_exceeded) def set_max_memory(size): soft, hard = resource.getrlimit(resource.RLIMIT_AS) resource.setrlimit(resource.RLIMIT_AS, (size, hard))
Здесь можно увидеть две опции: установку на максимальное процессорное время и максимальный предел используемой памяти.
При ограничении работы процессора необходимо извлечь мягкий и жёсткий лимиты для конкретного ресурса ( resource.RLIMIT_CPU ), а затем установить его значение. Для этого используется количество секунд, указанное в аргументе, и ранее полученное жёсткое ограничение. В конце нужно зарегистрировать сигнал, который будет отвечать за выход из системы, если процессорное время превышено.
Что касается памяти, то, как и в случае с процессором, устанавливаем мягкий и жёсткий лимиты. Для этого используется функция resource.setrlimit() с аргументом size и жёсткое ограничение, которое было получено.
Управляйте экспортом элементов.
Такие языки программирования, как Go, имеют механизм экспорта только для элементов (переменных, методов, интерфейсов) начинающихся с заглавной буквы. В Python подобного можно добиться с помощью переменной __all__ :
def foo(): pass def bar(): pass __all__ = ["bar"]
В данном случае, благодаря __all__ экспортирован будет не весь код, а только функция bar() . Кроме того, можно оставить переменную пустой, то при попытке импорта из этого модуля ничего не попадёт в экспорт, что приведёт к ошибке AttributeError .
Упростите использование операторов сравнения.
Использовать все операторы сравнения для одного класса может быть довольно сложно, учитывая, что их немало: __lt__ , __le__ , __gt__ или __ge__ . Но есть ли более простой способ сделать это? Здесь поможет functools.total_ordering :
from functools import total_ordering @total_ordering class Number: def __init__(self, value): self.value = value def __lt__(self, other): return self.value other.value def __eq__ (self, other): return self.value == other.value print(Number(20) > Number(3)) print(Number(1) Number(5)) print(Number(15) >= Number(15)) print(Number(10) Number(2))
Как это работает? Декоратор @total_ordering автоматически добавляет все остальные методы. В этом случае нужно только определить __lt__ и __eq__ , а все остальные пробелы за нас заполнит декоратор.
