Файл: Тема Основы языка Python.pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 04.12.2023

Просмотров: 139

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

77
В Питоне можно работать не только с отдельными элементами множеств, но и с множествами в целом. Так, для множеств определены некоторые операции (табл. 5).
Таблица 5
Операция
Описание
A | B
Объединение множеств
A & B
Пересечение множеств
A – B
Множество, элементы которого входят в A, но не входят в B
A ^ B
Элементы входят в A | B, но не входят в A & B
В результате этих операций создается новое множество, однако для них определена и сокращенная запись: |=, &=,
-=
и ^=. Такие операции изменяют множество, находящееся слева от знака операции.
Для множеств также определены операции сравнения (табл. 6).
Таблица 6
Операция
Описание
A == B
Все элементы совпадают
A != B
Есть различные элементы
A <= B
Все элементы A входят в B
A < B
A <= B и A != B
Также определены операции > и >=. Все групповые операции и сравнения проводятся над множествами за время, пропорциональное количеству элементов в множествах.
Словари
В жизни нередко возникает необходимость сопоставить ключу значение. Например, в англо-русском словаре английскому слову соответствует одно или несколько русских слов. Здесь английское слово является ключом, а русское – значением.

78
В языке Питон есть структура данных словарь, которая позволяет реализовывать подобные операции. При этом объекты-ключи уникальны и каждому из них сопоставлен некоторый объект-значение. Ограничения на ключи такие же, как на элементы множества, а вот значения могут быть и изменяемыми.
По сути, словарь является множеством, где каждому элементу-ключу сопоставлен еще и объект-значение.
Создать словарь в исходном тексте программы можно, записав в фигурных скобках пары «ключ – значение» через запятую, а внутри пары ключ отделяется от значения двоеточием (рис. 106).
Рис. 106. Пример работы со словарями
Добавлять пары «ключ – значение» в словарь очень просто: это делается по аналогии со списками (рис. 107).
Рис. 107. Пример работы со словарями
Пустой словарь можно создать, написав пустые фигурные скобки (это будет словарь, а не множество).
Словарь также можно конструировать из других объектов с помощью функции dict (рис. 108).

79
Рис. 108. Пример работы со словарями
На вход функции должен подавать iterable, каждый элемент которого, в свою очередь, является iterable строго с двумя элементами – ключом и значением.
Узнавать значение по ключу можно также с помощью записи ключа после имени словаря в квадратных скобках (рис. 109).
Рис. 109. Пример работы со словарями
Если такого ключа в словаре нет, то возникнет ошибка.
Удаление элемента из словаря делается специальной командой del. Это не функция. После слова del ставится пробел, затем пишется имя словаря, а затем, в квадратных скобках, удаляемый ключ (рис. 110).
Рис. 110. Пример работы со словарями
Проверка принадлежности ключа словарю осуществляется с помощью операции key in dictionary (точно так же, как проверка принадлежности элемента множеству).
Словарь является iterable и возвращает ключи в случайном порядке.
Например, код, представленный на рис. 111, напечатает содержимое словаря.


80
Рис. 111. Пример работы со словарями
Существует метод items, который возвращает iterable, содержащий в себе кортежи «ключ – значение» для всевозможных ключей (рис. 112).
Рис. 112. Пример работы со словарями
Когда нужно использовать словари
По прямому назначению: сопоставление ключа значению (названия дней недели, переводы слов и т. д.).
Для подсчета числа объектов. При очередной встрече объекта счетчик увеличивается на единицу. Это похоже на сортировку подсчётом.
Для хранения разреженных массивов. Например, если мы хотим хранить цену 92, 95 и 98 бензина, то могли бы создать массив из 99 элементов и хранить в нём. Но большая часть элементов не нужны – массив разреженный. Словарь здесь подходит больше.
Для подсчета числа элементов удобно использовать метод get. Он принимает два параметра: ключ, для которого нужно вернуть значения, и значение, которое будет возвращено, если такого ключа нет. Например, подсчитать, сколько раз входит в последовательность каждое из чисел, можно с помощью кода на рис. 113.

81
Рис. 113. Пример использования словарей
Полезные методы строк
isalpha – проверяет, что все символы строки являются буквами.
isdigit – проверяет, что все символы строки являются цифрами. isalnum – проверяет, что все символы строки являются буквами или цифрами. islower – проверяет, что все символы строки являются маленькими
(строчными) буквами. isupper – проверяет, что все символы строки являются большими
(заглавными, прописными) буквами. lstrip – обрезает все пробельные символы в начале строки. rstrip – обрезает все пробельные символы в конце строки. strip – обрезает все пробельные символы в начале и конце строки.
Пример решения сложной задачи на словари
Рассмотрим такую задачу: словарь задан в виде набора строк, в каждой строке записано слово на английском языке, затем следует символ -, затем, через запятую, перечислены возможные переводы слова на латынь.
Требуется составить латино-английский словарь и вывести его в том же виде. Все слова должны быть упорядочены по алфавиту. Возможные переводы одного слова должны быть также упорядочены по алфавиту.

82
На рис. 114 представлен набор данных для ввода.
Рис. 114. Входной набор данных
Вывод должен выглядеть так, как представлено на рис. 115.
Рис. 115. Требуемый выходной набор данных
Идея решения заключается в следующем: разрежем каждую строку на английское и латинские слова. Каждое из латинских слов возьмем в качестве ключа и добавим к его значениям английское слово (переводов может быть несколько). Затем пройдем по сортированным ключам и для каждого ключа выведем отсортированный список переводов (рис. 116).
Рис. 116. Программный код для решения задачи


83
2.3.
Использование
функций
языка
Python
для
обработки
последовательностей
Парадигмы
программирования
и
функциональное
программирование
Языки программирования предлагают различные средства для декомпозиции задачи. Существует несколько парадигм программирования:
− императивное (структурное, процедурное) программирование: программы являются последовательностью инструкций, которые могут читать и записывать данные из памяти. При изучении предыдущих тем мы пользовались, в основном, императивной парадигмой. Ряд языков, такие как
Паскаль (не Object Pascal) или C, являются яркими примерами императивных языков;
− декларативное программирование: описывается задача и ожидаемый результат, но не описываются пути её решения. Ярким примером является язык запросов к базам данных SQL: большая часть внутреннего устройства скрыта в СУБД, программист описывает только структуру базы данных и ожидаемый результат запросов;
− объектно-ориентированное программирование: программы манипулируют наборами объектов, при этом объекты обладают сохраняющимся во времени состоянием и методами для изменения этого состояния (или создания новых объектов). Мы познакомимся с ООП подробнее на одной из следующих лекций. Примером языка с объектно- ориентированной парадигмой является Java, ООП также поддерживается в
Питоне и C++;
− функциональное программирование: задача разбивается на набор функций. В идеале функции только принимают параметры и возвращают

84 значения, не изменяя состояния объектов или программы. Примером функциональных языков является Haskell.
Иногда выделяют и другие парадигмы программирования.
Некоторые языки предназначены для написания программ в основном в рамках одной парадигмы, другие же поддерживают несколько парадигм.
Например,
Питон и
C++ поддерживают различные парадигмы программирования. Разные части программы можно писать в разных парадигмах. Например, можно использовать функциональный стиль для обработки больших данных, объектно-ориентированный подход для реализации интерфейса и императивное программирование для промежуточной логики.
Функциональное программирование
Несмотря на то, что в функциональном стиле писать достаточно сложно и непривычно, функциональное программирование имеет массу плюсов:
− достаточно легко доказать формальную корректность алгоритмов. Хотя доказательство, даже для функциональных программ, часто намного длиннее, чем сама программа, но для фундаментальных алгоритмов, которые широко используются, лучше иметь формальное доказательство. Строить формальные доказательства императивных программ намного сложнее;
− для программ, написанных в функциональном стиле, легко проводить декомпозицию, отладку и тестирование. Когда вся программа разбита на функции, выполняющие элементарные действия, их разработка и проверка занимают намного меньше времени;
− для функциональных программ легко автоматически проводить распараллеливание, векторизацию и конвейеризацию.
Задача распараллеливания выполнения программ крайне актуальна сейчас, когда скорости ядер процессоров практически перестали расти.


85
Единственным способом ускорить выполнение программ является их параллельное вычисление на нескольких устройствах.
Последние успехи в решении задач машинного обучения связаны с использованием большого количества простых вычислительных устройств, например расчетов на видеокарте.
В функциональных программах не принято вносить изменения в объекты (и, вообще говоря, желательно, чтобы все объекты были неизменяемыми). Поэтому, если нам нужно посчитать результат вычисления двух функций, то во многих случаях можно делать это параллельно на разных ядрах процессора. Такое распараллеливание легко сделать автоматически.
Векторизация – выполнение одних и тех же действий над большим набором данных.
Тогда данные можно нарезать на куски и раскидать по разным вычислительным устройствам, а затем собрать результат вычислений в один объект.
Конвейеризация – это разбиение вычислений на несколько этапов, причём данные поступают на следующий этап обработки по времени готовности. Например, если нам нужно попарно перемножить значения в векторах A и B, а затем сложить со значениями в векторе C, то мы можем посчитать несколько первых результатов подсчета произведения и выполнять с ними сложение, не дожидаясь подсчета остальных значений. Это позволяет быстрее получить первые результаты и занять еще большее количество вычислительных устройств параллельно.
Встроенные функции для работы с последовательностями
В языке Питон есть много функций, которые принимают в качестве параметра iterable и могут сделать что-то полезное. С некоторыми из них, такими как sorted или map, мы уже немного знакомы. Рассмотрим еще некоторые из них. sum – находит сумму всех элементов iterable.

86 min, max – находит минимум и максимум в последовательности iterable. map – умеет принимать более двух параметров. Например, запись map(f, iterA, iterB) вернет iterable со значениями f(iterA[0], iterB[0]), f(iterA[1], iterB[1]), .... filter(predicate, iterable) – применяет функцию predicate ко всем элементам iterable и возвращает iterable, который содержит только те элементы, которые удовлетворяли предикаты (т. е. функция predicate вернула
True). Например, решение задачи о поиске минимального положительного элемента в списке может выглядеть так, как представлено на рис. 117.
Рис. 117. Использование функции filter
Здесь в качестве предиката использована лямбда-функция, которая возвращает True при значении параметра больше 0, а в качестве входного iterable – результат вызова map для функции int и нарезанного на слова ввода.
Функция min применена к тому iterable, который был возвращен функцией filter. enumerate – возвращает кортежи из номера элемента (при нумерации с нуля) и значения очередного элемента. С помощью enumerate, например, удобно перебирать элементы iterable (доступ по индексу в которых невозможен) и выводить номера элементов, которые обладают некоторым свойством (рис. 118).
Рис. 118. Использование функции enumerate


87 any, all – возвращают истину, если хотя бы один или все элементы iterable истинны соответственно. Например, так, как показано на рис. 119, можно проверить, не превышают ли все члены последовательности 100 по модулю.
Рис. 119. Использование функции all zip(iterA, iterB, ...) – конструирует кортежи из элементов (iterA[0], iterB[0], ...), (iterA[1], iterB[1], ...), ....
Пример решения сложной задачи в функциональном стиле
С помощью этих функций можно решить достаточно сложные задачи без использования циклов и условных операторов. Например, задачу по сортировкам про такси: в первой строке задано количество людей и автомобилей такси, в следующих двух строках расстояние в километрах для каждого человека и цена за километр для каждого такси. Необходимо сопоставить каждого человека и номер такси так, чтобы суммарная цена поездок была минимальна. Идея решения заключается в том, что люди, которым ехать дальше, должны ехать на более дешевых такси (рис. 120).
Рис. 120. Исходная программа

88
А теперь избавимся от переменных, подставляя на их места исходные значения. Сделаем переносы для удобного восприятия (рис. 121).
Рис. 121. Функциональный стиль написания программы
Это та же программа!
itertools, functools
Генерация комбинаторных объектов itertools
В Питоне есть библиотека itertools, которая содержит много функций для работы с итерируемыми объектами. С этими функциями можно ознакомиться в официальной документации к языку.
Нам наиболее интересны функции, генерирующие комбинаторные объекты. itertools.combinations(iterable, size) генерирует все подмножества множества iterable размером size в виде кортежей. Это может быть использовано вместо вложенных циклов при организации перебора.

89
Например, мы можем неэффективно решить задачу о поиске трех чисел в последовательности, дающих наибольшее произведение (рис. 122).
Рис. 122. Генерация комбинаторных объектов itertools.permutations(iterable) генерирует все перестановки iterable.
Существует вариант функции с двумя параметрами, второй параметр является размером подмножества. Тогда генерируются все перестановки всех подмножеств заданного размера. itertools.combinations_with_replacement(iterable, size) генерирует все подмножества iterable размером size с повторениями, т. е. одно и то же число может входить в подмножество несколько раз.
Модуль functools и функции partial, reduce, accumulate
Модуль functools содержит некоторые функции, которые могут быть полезны для обработки последовательностей и не только.
Функция functools.partial предназначена для оборачивания существующих функций с подстановкой некоторых параметров. Например, мы можем создать функцию для печати в файл, чтобы каждый раз не указывать какие-то параметры. Например, существует вариант функции int с двумя параметрами: первый – это переменная, которую необходимо преобразовать в число, второй – система счисления, в которой записано число. С помощью partial мы можем создать функцию-обёртку, преобразующую строки из 0 и 1 в числа (рис. 123).