Неужели не нашлось русских специалистов, чтобы записать курс по пайтону ? Да, можно включить переводчик и слушать с переводом, но это что? Это кто-то считает хорошим и понятным курсом для начинающих? |
Заключение
Смотреть на youtube
Проект для лекции Lecture11.rar.
Это заключительная лекция нашего курса "Основы программирования на языке Python". В предыдущих лекциях мы показали, что Python является мощным языком программирования. Он обладает такими встроенными структурами данных как списки, кортежи, множества, словари, файлы. В большинстве языков программирования такие структуры определены на уровне дополнительных библиотек, не являясь частью языка программирования. Управляющие структуры языка Python - структуры выбора, организации циклов, определение процедур и функций, рекурсия - не уступают возможностям других языков программирования. Владея этими средствами, можно создавать проекты на первом уровне программирования - на уровне программирования в процедурах и функциях.
В наших лекциях мы подробно рассмотрели возможности реализации на языке Python современных технологий программирования - модульного и объектно-ориентированного программирования. Владея основами программирования на языке Python, можно создавать проекты на более высоком уровне программирования - на уровне программирования в модулях и классах.
Однако серьезные проекты требуют перехода на следующий уровень программирования - программирования в пакетах Python, уровень, не рассматриваемый в рамках нашего курса. Освоение этого уровня требует отдельного курса.
В заключительной лекции мы обсудим слабое место языка Python, и покажем, как эта слабость преодолевается. В этой лекции нас будет интересовать эффективность программ, разработанных на языке Python.
Среди критериев, которым должен удовлетворять программный проект, эффективность, зачастую, стоит на последнем месте. Благодаря мощи современных компьютеров многие проекты выполняются за время, приемлемое для пользователя, уменьшать это время особого смысла не имеет. Другие критерии - корректность, устойчивость, повторное использование, простота понимания и простата сопровождения играют более важную роль чем эффективность проекта.
Но проект проекту рознь. Есть критически важные проекты, где эффективность выходит на первое место. Если прогноз погоды на завтра будет получен послезавтра, то ценность такого прогноза минимальна. Для проектов, работающих в реальном времени, проектов, работающих с большими данными эффективность крайне важна.
Если мы создаем приложение на чистом Python, то трудно рассчитывать, что оно будет столь же эффективным по времени выполнения как приложение, написанное на компилируемых языках, например, C++, C# или Java. Приложение Python выполняется в интерпретируемом режиме. Интерпретатор имеет свои достоинства. Можно выполнять приложение в пошаговом режиме, следить за ходом вычислений. Платой за это является время. Конечно, мы привыкли, что обычно при создании проекта на других языках отладка приложения ведется в режиме интерпретатора, а отлаженное приложение компилируется и выполняется как исполняемый (exe) файл. У Python компилятора нет. Здесь эффективность выполнения достигается другими средствами.
Давайте посмотрим, насколько Python проигрывает по времени выполнения на классических задачах.
Для проведения исследований я создал новый проект и добавил в него класс Linear, в который поместил несколько классических методов. Начну с двух методов, позволяющих создать вектор и матрицу, заполненные целыми случайными числами из заданного диапазона. Для получения случайных чисел импортирую пакет random. Роль вектора играет список, содержащий элементы вектора, а роль прямоугольной матрицы - список списков, элементы внешнего списка являются списками, задающими строки матрицы.
Вот начальный код этого класса:
class Linear(object): """ Методы создания random матриц Методы линейной алгебры Методы сортировки массивов Все методы декорированы декоратором timer """
Поскольку эта лекция посвящена оценке эффективности, то первым делом в этот класс я поместил декоратор timer с параметром, позволяющим измерять время работы декорируемой функции при повторении ее вызова заданное число раз. Этот декоратор мы построили в предыдущей лекции, сейчас же приведу его текст без дополнительных комментариев:
def timer(repeat = 1): """ Обертка декоратора timer """ def decor_timer(func): """ измеряет время работы функции при вызове функции repeat раз """ from time import time def wrapper(*args, **kwargs): start = time() for i in range(repeat): res = func(*args, **kwargs) fin = time() L = [] L.append(fin - start) L.append(res) return L return wrapper return decor_timer
Построим теперь два метода, генерирующие создание вектора и матрицы заданных размеров, заполненных случайными числами:
@timer(repeat = 10) def CreateVector(self, n:int, min:int, max:int)->list: """ Создание random вектора (списка) размера n Элементы целые числа из диапазона [min, max] """ from random import randint return [randint(min, max) for i in range(n)] @timer(repeat = 10) def CreateMatrix(self, n:int, m:int, min:int, max:int)->list: """ Создание random матрицы (списка) размера n * m Элементы целые числа из диапазона [min, max] """ from random import randint return [[randint(min, max) for j in range(m)] for i in range(n)]
Методы декорированы декоратором timer. Реализация методов использует инструмент генератор, что повышает эффективность методов.
Наряду с классом Linear я построил тестирующий модуль, в котором создаю тесты, позволяющие выполнять методы класса Linear. Вот первый тест, позволяющий измерить время создания вектора:
def test1(): lin = Linear() L = lin.CreateVector(10000, -100, 100) print('Время создания вектора размера 10000: ', L[0]) L = lin.CreateVector(100000, -100, 100) print('Время создания вектора размера 100000: ', L[0]) L = lin.CreateVector(1000000, -100, 100) print('Время создания вектора размера 1000000: ', L[0])
Ниже показано время (в секундах) десятикратного создания вектора в Python и в C#. Здесь и далее все оценки времени приведены для моего компьютера, на котором я сейчас работаю. Я не стану приводить код проектов на С#, поскольку это потребовало бы дополнительных пояснений, связанных с особенностями программирования на C#.
n | 10000 | 100000 | 1000000 |
vector Python | 0.094 | 0.872 | 8.838 |
vector C | 0,010 | 0,020 | 0,201 |
Тест, позволяющий вычислить время создания матрицы, не привожу, поскольку он является понятной вариацией теста 1.
В таблице 2 показано время десятикратного создания квадратной матрицы в Python и в C#.
n | 100 | 500 | 1000 |
matrix Python | 0,093 | 2,161 | 8,700 |
matrix C# | 0,003 | 0,056 | 0,226 |
Анализ результатов производительности показывает, что Python проигрывает C# примерно два порядка и это замедление становится ощутимым при создании векторов и матриц большого размера.
Проведем сравнение для еще нескольких классических алгоритмов. Вот код на Python метода пузырьковой сортировки:
@timer(repeat = 1) def BubbleSort(self, ar): """ Алгоритм пузырьковой сортировки """ n = len(ar) for i in range(n-1): for j in range(n-1, i, -1): if ar[j] < ar[j-1]: ar[j], ar[j-1] = ar[j-1], ar[j]
Простой алгоритм квадратичной сложности. В таблице 3 приведены соответствующие оценки времени.
2 | 100 | 500 | 1000 | 10000 |
BubbleSort Python | 0.001 | 0.016 | 0.070 | 6.969 |
BubbleSort C# | 0.0001 | 0.0002 | 0.002 | 0.288 |
И здесь Python проигрывает те же два порядка. Посмотрим, как он работает на быстрой сортировке, где требуется реализовать рекурсию. Вот код Python алгоритма быстрой сортировки:
def QuickSort(self, ar, start, finish): if start < finish: left, right, mid = start, finish, (start + finish)//2 item = ar[mid] while left <= right: while ar[left] < item: left+=1 while ar[right] > item: right -=1 if left <= right: ar[left], ar[right] = ar[right], ar[left] left +=1; right -=1 self.QuickSort(ar, start, right) self.QuickSort(ar, left, finish) def QSort(self, ar): from time import time start = time() self.QuickSort(ar, 0, len(ar) - 1) finish = time() print('QuickSort: n = ', len(ar), ' time = ', finish - start)
Заметьте, декорировать рекурсивную функцию не следует из-за многократного декорирования всех внутренних вызовов. Поэтому измерение времени встроено непосредственно в нерекурсивную обертку рекурсивного метода сортировки.
Коды тестов я уже не привожу, поскольку они строятся по одному образцу. В таблице 4 приведены соответствующие оценки времени.
n | 1000 | 10000 | 100000 | 1000000 |
QuickSort Python | 0.001 | 0.017 | 0.214 | 2.59 |
QuickSort C# | 0.00001 | 0.001 | 0.00903 | 0.098 |
Заметьте, что времена порядка 0,001 сравнимы с погрешностью измерения времени. Отрадно отметить, что интерпретатор Python хорошо справляется с рекурсией и проигрыш Python не возрос в сравнении с нерекурсивным алгоритмом.
В заключение сравнения относительных оценок производительности рассмотрим алгоритм умножения матриц, имеющий сложность и работающий с двумерным массивом (списком списков в нашей реализации).
Вот код соответствующего алгоритма:
@timer(repeat = 1) def MultMatr(self, A, B): """ Умножение матриц: C = A * B """ n = len(A) p = len(A[0]) m = len(B[0]) C = [] for i in range(n): v = [] for j in range(m): item = 0 for k in range((p)): item += A[i] [k] * B[k][j] v.append(item) C.append(v) return C
В таблице 5 приведены оценки времени умножения двух квадратных матриц размера n*n.
n | 100 | 200 | 500 |
Multmatr Python | 0.124 | 0.946 | 16.211 |
Multmatr C# | 0.006 | 0.046 | 0.814 |
Подводя промежуточный итог, можно заметить, что создавать приложение на чистом Python (без использования специальных пакетов) следует тогда, когда время выполнения программы не играет существенной роли, поскольку, например, вам предстоит решать задачи относительно небольшого размера. Преимущество Python в этом случае в том, что время разработки программы на Python может быть меньше, чем на других языках программирования.
Как эффективность выполнения достигается в Python
Что же предлагает Python, когда время выполнения критично. Одно из решений состоит в том, что для некоторых важных задач Python предлагает эффективно реализованные методы. Например, для сортировки структур данных Python предлагает функцию sorted, применимую ко всем итерируемым объектам, которая по эффективности сравнима с эффективностью быстрой сортировки массива, написанной на языке C#.
Главное достижение Python, позволяющее создавать эффективные приложения для решения сложных и важных задач - это работа с пакетами. Для Python написано большое число пакетов для решения задач в самых разных прикладных областях:
- Извлечение смысла из текстов на естественных языках (задачи NLP - Natural Language Processing).
- Нейронные сети и глубокое обучение.
- Извлечение знаний из данных (методы Data Mining)
- Многие другие прикладные задачи.
Эти эффективно работающие пакеты пишутся, конечно же, не на Python, а на компилируемых языках, часто на С, что позволяет достигать максимальной эффективности. Недостаток Python с точки зрения классики - бестиповость данных - превращается в его достоинство. Python служит как клей, позволяющий склеивать в единое целое методы, написанные на разных языках.
Многие пакеты встроены в стандартную библиотеку пакетов Python, другие легко добавляются в библиотеку сторонних модулей. Существующий и постоянно расширяющийся набор пакетов Python является гарантией долгой жизни этого языка.
На этой позитивной ноте я заканчиваю курс "Основы программирования на языке Python". Надеюсь, курс будет полезен для тех, кто создает проекты Python как на уровне программирования в процедурах и функциях, так и на уровне программирования в модулях и классах. Для перехода на следующий уровень требуется отдельный курс "Программирование в пакетах Python".