ээдд. Прохоренок_Н_А__Дронов_В_А_Python_3_и_PyQt_5_Разработка_приложен. Николай Прохоренок Владимир Дронов
Скачать 7.92 Mb.
|
")) input() Результат выполнения: Метод subn() аналогичен методу sub() , но возвращает не строку, а кортеж из двух элемен- тов: измененной строки и количества произведенных замен. Метод имеет следующий фор- мат: subn(<Новый фрагмент или ссылка на функцию>, <Строка для замены> [, <Максимальное количество замен>]) Заменим все числа в строке на 0 : >>> p = re.compile(r"[0-9]+") >>> p.subn("0", "2008, 2009, 2010, 2011") ('0, 0, 0, 0', 4) Вместо метода subn() можно воспользоваться функцией subn() . Формат функции: re.subn(<Шаблон>, <Новый фрагмент или ссылка на функцию>, <Строка для замены>[, <Максимальное количество замен> [, flags=0]]) Глава 7. Регулярные выражения 139 В качестве параметра <Шаблон> можно указать строку с регулярным выражением или ском- пилированное регулярное выражение: >>> p = r"200[79]" >>> re.subn(p, "2001", "2007, 2008, 2009, 2010") ('2001, 2008, 2001, 2010', 2) Для выполнения замен также можно использовать метод expand() , поддерживаемый объек- том Match . Формат метода: expand(<Шаблон>) Внутри указанного шаблона можно использовать обратные ссылки: \номер группы , \g<номер группы> и \g<название группы> : >>> p = re.compile(r"<(?P >>> m = p.search(" ") >>> m.expand(r"<\2><\1>") # \номер ' ' >>> m.expand(r"<\g<2>><\g<1>>") # \g<номер> ' ' >>> m.expand(r"<\g ' ' 7.5. Прочие функции и методы Метод split() разбивает строку по шаблону и возвращает список подстрок. Его формат: split(<Исходная строка>[, <Лимит>]) Если во втором параметре задано число, то в списке окажется указанное количество под- строк. Если подстрок больше указанного количества, то список будет содержать еще один элемент — с остатком строки: >>> import re >>> p = re.compile(r"[\s,.]+") >>> p.split("word1, word2\nword3\r\nword4.word5") ['word1', 'word2', 'word3', 'word4', 'word5'] >>> p.split("word1, word2\nword3\r\nword4.word5", 2) ['word1', 'word2', 'word3\r\nword4.word5'] Если разделитель в строке не найден, список будет состоять только из одного элемента, содержащего исходную строку: >>> p = re.compile(r"[0-9]+") >>> p.split("word, word\nword") ['word, word\nword'] Вместо метода split() можно воспользоваться функцией split() . Формат функции: re.split(<Шаблон>, <Исходная строка>[, <Лимит>[, flags=0]]) В качестве параметра <Шаблон> можно указать строку с регулярным выражением или ском- пилированное регулярное выражение: >>> p = re.compile(r"[\s,.]+") >>> re.split(p, "word1, word2\nword3") ['word1', 'word2', 'word3'] 140 Часть I. Основы языка Python >>> re.split(r"[\s,.]+", "word1, word2\nword3") ['word1', 'word2', 'word3'] Функция escape(<Строка>) экранирует все специальные символы в строке, после чего ее можно безопасно использовать внутри регулярного выражения: >>> print(re.escape(r"[]().*")) \[\]\(\)\.\* Функция purge() выполняет очистку кэша, в котором хранятся промежуточные данные, используемые в процессе выполнения регулярных выражений. Ее рекомендуется вызывать после обработки большого количества регулярных выражений. Результата эта функция не возвращает: >>> re.purge() ГЛ А В А 8 Списки, кортежи, множества и диапазоны Списки, кортежи, множества и диапазоны — это нумерованные наборы объектов. Каж- дый элемент набора содержит лишь ссылку на объект — по этой причине они могут содер- жать объекты произвольного типа данных и иметь неограниченную степень вложенности. Позиция элемента в наборе задается индексом. Обратите внимание на то, что нумерация элементов начинается с 0 , а не с 1 Списки и кортежи являются просто упорядоченными последовательностями элементов. Как и все последовательности, они поддерживают обращение к элементу по индексу, получение среза, конкатенацию (оператор + ), повторение (оператор * ), проверку на вхождение (оператор in ) и невхождение (оператор not in ). Списки относятся к изменяемым типам данных. Это означает, что мы можем не только получить элемент по индексу, но и изменить его: >>> arr = [1, 2, 3] # Создаем список >>> arr[0] # Получаем элемент по индексу 1 >>> arr[0] = 50 # Изменяем элемент по индексу >>> arr [50, 2, 3] Кортежи относятся к неизменяемым типам данных. Иными словами, можно получить элемент по индексу, но изменить его нельзя: >>> t = (1, 2, 3) # Создаем кортеж >>> t[0] # Получаем элемент по индексу 1 >>> t[0] = 50 # Изменить элемент по индексу нельзя! Traceback (most recent call last): File " ", line 1, in TypeError: 'tuple' object does not support item assignment Множества могут быть как изменяемыми, так и неизменяемыми. Их основное отличие от только что рассмотренных типов данных — хранение лишь уникальных значений (неуникальные значения автоматически отбрасываются): >>> set([0, 1, 1, 2, 3, 3, 4]) {0, 1, 2, 3, 4} 142 Часть I. Основы языка Python Что касается диапазонов, то они представляют собой наборы чисел, сформированные на основе заданных начального, конечного значений и величины шага между числами. Их важнейшее преимущество перед всеми остальными наборами объектов — небольшой объем занимаемой оперативной памяти: >>> r = range(0, 101, 10) >>> for i in r: print(i, end = " ") 0 10 20 30 40 50 60 70 80 90 100 Рассмотрим все упомянутые типы данных более подробно. 8.1. Создание списка Создать список можно следующими способами: с помощью функции list([<Последовательность>]) . Функция позволяет преобразовать любую последовательность в список. Если параметр не указан, создается пустой список: >>> list() # Создаем пустой список [] >>> list("String") # Преобразуем строку в список ['S', 't', 'r', 'i', 'n', 'g'] >>> list((1, 2, 3, 4, 5)) # Преобразуем кортеж в список [1, 2, 3, 4, 5] указав все элементы списка внутри квадратных скобок: >>> arr = [1, "str", 3, "4"] >>> arr [1, 'str', 3, '4'] заполнив список поэлементно с помощью метода append() : >>> arr = [] # Создаем пустой список >>> arr.append(1) # Добавляем элемент1 (индекс 0) >>> arr.append("str") # Добавляем элемент2 (индекс 1) >>> arr [1, 'str'] В некоторых языках программирования (например, в PHP) можно добавить элемент, указав пустые квадратные скобки или индекс больше последнего индекса. В языке Python все эти способы приведут к ошибке: >>> arr = [] >>> arr[] = 10 SyntaxError: invalid syntax >>> arr[0] = 10 Traceback (most recent call last): File " ", line 1, in IndexError: list assignment index out of range При создании списка в переменной сохраняется ссылка на объект, а не сам объект. Это обя- зательно следует учитывать при групповом присваивании. Групповое присваивание можно использовать для чисел и строк, но для списков этого делать нельзя. Рассмотрим пример: Глава 8. Списки, кортежи, множества и диапазоны 143 >>> x = y = [1, 2] # Якобы создали два объекта >>> x, y ([1, 2], [1, 2]) В этом примере мы создали список из двух элементов и присвоили значение переменным x и y . Теперь попробуем изменить значение в переменной y : >>> y[1] = 100 # Изменяем второй элемент >>> x, y # Изменилось значение сразу в двух переменных ([1, 100], [1, 100]) Как видно из примера, изменение значения в переменной y привело также к изменению значения в переменной x . Таким образом, обе переменные ссылаются на один и тот же объ- ект, а не на два разных объекта. Чтобы получить два объекта, необходимо производить раз- дельное присваивание: >>> x, y = [1, 2], [1, 2] >>> y[1] = 100 # Изменяем второй элемент >>> x, y ([1, 2], [1, 100]) Точно такая же ситуация возникает при использовании оператора повторения * . Например, в следующей инструкции производится попытка создания двух вложенных списков с по- мощью оператора * : >>> arr = [ [] ] * 2 # Якобы создали два вложенных списка >>> arr [[], []] >>> arr[0].append(5) # Добавляем элемент >>> arr # Изменились два элемента [[5], [5]] Создавать вложенные списки следует с помощью метода append() внутри цикла: >>> arr = [] >>> for i in range(2): arr.append([]) >>> arr [[], []] >>> arr[0].append(5); arr [[5], []] Можно также воспользоваться генераторами списков: >>> arr = [ [] for i in range(2) ] >>> arr [[], []] >>> arr[0].append(5); arr [[5], []] Проверить, ссылаются ли две переменные на один и тот же объект, позволяет оператор is Если переменные ссылаются на один и тот же объект, оператор is возвращает значение True : >>> x = y = [1, 2] # Неправильно >>> x is y # Переменные содержат ссылку на один и тот же список True 144 Часть I. Основы языка Python >>> x, y = [1, 2], [1, 2] # Правильно >>> x is y # Это разные объекты False Но что же делать, если необходимо создать копию списка? Первый способ заключается в применении операции извлечения среза, второй — в использовании функции list() , а третий — в вызове метода copy() : >>> x = [1, 2, 3, 4, 5] # Создали список >>> # Создаем копию списка >>> y = list(x) # или с помощью среза: y = x[:] >>> # или вызовом метода copy(): y = x.copy() >>> y [1, 2, 3, 4, 5] >>> x is y # Оператор показывает, что это разные объекты False >>> y[1] = 100 # Изменяем второй элемент >>> x, y # Изменился только список в переменной y ([1, 2, 3, 4, 5], [1, 100, 3, 4, 5]) На первый взгляд может показаться, что мы получили копию — оператор is показывает, что это разные объекты, а изменение элемента затронуло лишь значение переменной y В данном случае вроде все нормально. Но проблема заключается в том, что списки в языке Python могут иметь неограниченную степень вложенности. Рассмотрим это на примере: >>> x = [1, [2, 3, 4, 5]] # Создали вложенный список >>> y = list(x) # Якобы сделали копию списка >>> x is y # Разные объекты False >>> y[1][1] = 100 # Изменяем элемент >>> x, y # Изменение затронуло переменную x!!! ([1, [2, 100, 4, 5]], [1, [2, 100, 4, 5]]) Здесь мы создали список, в котором второй элемент является вложенным списком, после чего с помощью функции list() попытались создать копию списка. Как и в предыдущем примере, оператор is показывает, что это разные объекты, но посмотрите на результат — изменение переменной y затронуло и значение переменной x . Таким образом, функция list() и операция извлечения среза создают лишь поверхностную копию списка. Чтобы получить полную копию списка, следует воспользоваться функцией deepcopy() из модуля copy : >>> import copy # Подключаем модуль copy >>> x = [1, [2, 3, 4, 5]] >>> y = copy.deepcopy(x) # Делаем полную копию списка >>> y[1][1] = 100 # Изменяем второй элемент >>> x, y # Изменился только список в переменной y ([1, [2, 3, 4, 5]], [1, [2, 100, 4, 5]]) Функция deepcopy() создает копию каждого объекта, при этом сохраняя внутреннюю структуру списка. Иными словами, если в списке существуют два элемента, ссылающиеся на один объект, то будет создана копия объекта, и элементы будут ссылаться на этот новый объект, а не на разные объекты: >>> import copy # Подключаем модуль copy >>> x = [1, 2] Глава 8. Списки, кортежи, множества и диапазоны 145 >>> y = [x, x] # Два элемента ссылаются на один объект >>> y [[1, 2], [1, 2]] >>> z = copy.deepcopy(y) # Сделали копию списка >>> z[0] is x, z[1] is x, z[0] is z[1] (False, False, True) >>> z[0][0] = 300 # Изменили один элемент >>> z # Значение изменилось сразу в двух элементах! [[300, 2], [300, 2]] >>> x # Начальный список не изменился [1, 2] 8.2. Операции над списками Обращение к элементам списка осуществляется с помощью квадратных скобок, в которых указывается индекс элемента. Нумерация элементов списка начинается с нуля. Выведем все элементы списка: >>> arr = [1, "str", 3.2, "4"] >>> arr[0], arr[1], arr[2], arr[3] (1, 'str', 3.2, '4') С помощью позиционного присваивания можно присвоить значения элементов списка каким-либо переменным. Количество элементов справа и слева от оператора = должно сов- падать, иначе будет выведено сообщение об ошибке: >>> x, y, z = [1, 2, 3] # Позиционное присваивание >>> x, y, z (1, 2, 3) >>> x, y = [1, 2, 3] # Количество элементов должно совпадать Traceback (most recent call last): File " ", line 1, in ValueError: too many values to unpack (expected 2) В Python 3 при позиционном присваивании перед одной из переменных слева от операто- ра = можно указать звездочку. В этой переменной будет сохраняться список, состоящий из «лишних» элементов. Если таких элементов нет, список будет пустым: >>> x, y, *z = [1, 2, 3]; x, y, z (1, 2, [3]) >>> x, y, *z = [1, 2, 3, 4, 5]; x, y, z (1, 2, [3, 4, 5]) >>> x, y, *z = [1, 2]; x, y, z (1, 2, []) >>> *x, y, z = [1, 2]; x, y, z ([], 1, 2) >>> x, *y, z = [1, 2, 3, 4, 5]; x, y, z (1, [2, 3, 4], 5) >>> *z, = [1, 2, 3, 4, 5]; z [1, 2, 3, 4, 5] 146 Часть I. Основы языка Python Так как нумерация элементов списка начинается с 0 , индекс последнего элемента будет на единицу меньше количества элементов. Получить количество элементов списка позволяет функция len() : >>> arr = [1, 2, 3, 4, 5] >>> len(arr) # Получаем количество элементов 5 >>> arr[len(arr)-1] # Получаем последний элемент 5 Если элемент, соответствующий указанному индексу, отсутствует в списке, возбуждается исключение IndexError : >>> arr = [1, 2, 3, 4, 5] >>> arr[5] # Обращение к несуществующему элементу Traceback (most recent call last): File " ", line 1, in IndexError: list index out of range В качестве индекса можно указать отрицательное значение. В этом случае смещение будет отсчитываться от конца списка, а точнее — чтобы получить положительный индекс, значе- ние вычитается из общего количества элементов списка: >>> arr = [1, 2, 3, 4, 5] >>> arr[-1], arr[len(arr)-1] # Обращение к последнему элементу (5, 5) Так как списки относятся к изменяемым типам данных, мы можем изменить элемент по индексу: >>> arr = [1, 2, 3, 4, 5] >>> arr[0] = 600 # Изменение элемента по индексу >>> arr [600, 2, 3, 4, 5] Кроме того, списки поддерживают операцию извлечения среза, которая возвращает указан- ный фрагмент списка. Формат операции: [<Начало>:<Конец>:<Шаг>] Все параметры не являются обязательными. Если параметр <Начало> не указан, использует- ся значение 0 . Если параметр <Конец> не указан, возвращается фрагмент до конца списка. Следует также заметить, что элемент с индексом, указанным в этом параметре, не входит в возвращаемый фрагмент. Если параметр <Шаг> не указан, используется значение 1 . В ка- честве значения параметров можно указать отрицательные значения. Теперь рассмотрим несколько примеров: сначала получим поверхностную копию списка: >>> arr = [1, 2, 3, 4, 5] >>> m = arr[:]; m # Создаем поверхностную копию и выводим значения [1, 2, 3, 4, 5] >>> m is arr # Оператор is показывает, что это разные объекты False Глава 8. Списки, кортежи, множества и диапазоны 147 затем выведем символы в обратном порядке: >>> arr = [1, 2, 3, 4, 5] >>> arr[::-1] # Шаг -1 [5, 4, 3, 2, 1] выведем список без первого и последнего элементов: >>> arr[1:] # Без первого элемента [2, 3, 4, 5] >>> arr[:-1] # Без последнего элемента [1, 2, 3, 4] получим первые два элемента списка: >>> arr[0:2] # Символ с индексом 2 не входит в диапазон [1, 2] а так получим последний элемент: >>> arr[-1:] # Последний элемент списка [5] и, наконец, выведем фрагмент от второго элемента до четвертого включительно: >>> arr[1:4] # Возвращаются элементы с индексами 1, 2 и 3 [2, 3, 4] С помощью среза можно изменить фрагмент списка. Если срезу присвоить пустой список, то элементы, попавшие в срез, будут удалены: >>> arr = [1, 2, 3, 4, 5] >>> arr[1:3] = [6, 7] # Изменяем значения элементов с индексами 1 и 2 >>> arr [1, 6, 7, 4, 5] >>> arr[1:3] = [] # Удаляем элементы с индексами 1 и 2 >>> arr [1, 4, 5] Объединить два списка в один список позволяет оператор + . Результатом объединения будет новый список: >>> arr1 = [1, 2, 3, 4, 5] >>> arr2 = [6, 7, 8, 9] >>> arr3 = arr1 + arr2 >>> arr3 [1, 2, 3, 4, 5, 6, 7, 8, 9] Вместо оператора + можно использовать оператор += . Следует учитывать, что в этом случае элементы добавляются в текущий список: >>> arr = [1, 2, 3, 4, 5] >>> arr += [6, 7, 8, 9] >>> arr [1, 2, 3, 4, 5, 6, 7, 8, 9] Кроме рассмотренных операций, списки поддерживают операцию повторения и проверку на вхождение. Повторить список указанное количество раз можно с помощью оператора * , а выполнить проверку на вхождение элемента в список позволяет оператор in : 148 Часть I. Основы языка Python >>> [1, 2, 3] * 3 # Операция повторения [1, 2, 3, 1, 2, 3, 1, 2, 3] >>> 2 in [1, 2, 3, 4, 5], 6 in [1, 2, 3, 4, 5] # Проверка на вхождение (True, False) 8.3. Многомерные списки Любой элемент списка может содержать объект произвольного типа. Например, элемент списка может быть числом, строкой, списком, кортежем, словарем и т. д. Создать вложен- ный список можно, например, так: >>> arr = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ] Как вы уже знаете, выражение внутри скобок может располагаться на нескольких строках. Следовательно, предыдущий пример можно записать иначе: >>> arr = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ] Чтобы получить значение элемента во вложенном списке, следует указать два индекса: >>> arr[1][1] 5 Элементы вложенного списка также могут иметь элементы произвольного типа. Количество вложений не ограничено, и мы можем создать объект любой степени сложности. В этом случае для доступа к элементам указывается несколько индексов подряд: >>> arr = [ [1, ["a", "b"], 3], [4, 5, 6], [7, 8, 9] ] >>> arr[0][1][0] 'a' >>> arr = [ [1, { "a": 10, "b": ["s", 5] } ] ] >>> arr[0][1]["b"][0] 's' 8.4. Перебор элементов списка Перебрать все элементы списка можно с помощью цикла for : >>> arr = [1, 2, 3, 4, 5] >>> for i in arr: print(i, end=" ") 1 2 3 4 5 Следует заметить, что переменную i внутри цикла можно изменить, но если она ссылается на неизменяемый тип данных (например, на число или строку), это не отразится на исход- ном списке: >>> arr = [1, 2, 3, 4] # Элементы имеют неизменяемый тип (число) >>> for i in arr: i += 10 >>> arr # Список не изменился [1, 2, 3, 4] Глава 8. Списки, кортежи, множества и диапазоны 149 >>> arr = [ [1, 2], [3, 4] ] # Элементы имеют изменяемый тип (список) >>> for i in arr: i[0] += 10 >>> arr # Список изменился [[11, 2], [13, 4]] Для генерации индексов элементов можно воспользоваться функцией range() , которая воз- вращает объект-диапазон, поддерживающий итерации. С помощью такого диапазона внут- ри цикла for можно получить текущий индекс. Функция range() имеет следующий формат: range([<Начало>, ]<Конец>[, <Шаг>]) Первый параметр задает начальное значение. Если он не указан, используется значение 0 Во втором параметре указывается конечное значение. Следует заметить, что это значение не входит в возвращаемый диапазон значений. Если параметр <Шаг> не указан, используется значение 1 . Для примера умножим каждый элемент списка на 2: arr = [1, 2, 3, 4] for i in range(len(arr)): arr[i] *= 2 print(arr) # Результат выполнения: [2, 4, 6, 8] Можно также воспользоваться функцией enumerate(<Объект>[, start=0]) , которая на каж- дой итерации цикла for возвращает кортеж из индекса и значения текущего элемента спи- ска. Умножим каждый элемент списка на 2: arr = [1, 2, 3, 4] for i, elem in enumerate(arr): arr[i] *= 2 print(arr) # Результат выполнения: [2, 4, 6, 8] Кроме того, перебрать элементы можно с помощью цикла while , но нужно помнить, что он выполняется медленнее цикла for . Для примера умножим каждый элемент списка на 2, ис- пользуя цикл while : arr = [1, 2, 3, 4] i, c = 0, len(arr) while i < c: arr[i] *= 2 i += 1 print(arr) # Результат выполнения: [2, 4, 6, 8] 8.5. Генераторы списков и выражения-генераторы В предыдущем разделе мы изменяли элементы списка следующим образом: arr = [1, 2, 3, 4] for i in range(len(arr)): arr[i] *= 2 print(arr) # Результат выполнения: [2, 4, 6, 8] С помощью генераторов списков тот же самый код можно записать более компактно, к то- му же генераторы списков работают быстрее цикла for . Однако вместо изменения исходно- го списка возвращается новый список: arr = [1, 2, 3, 4] arr = [ i * 2 for i in arr ] print(arr) # Результат выполнения: [2, 4, 6, 8] 150 Часть I. Основы языка Python Как видно из примера, мы поместили цикл for внутри квадратных скобок, а также измени- ли порядок следования параметров, — инструкция, выполняемая внутри цикла, находится перед циклом. Обратите внимание и на то, что выражение внутри цикла не содержит опера- тора присваивания, — на каждой итерации цикла будет генерироваться новый элемент, которому неявным образом присваивается результат выполнения выражения внутри цикла. В итоге будет создан новый список, содержащий измененные значения элементов исходного списка. Генераторы списков могут иметь сложную структуру — например, состоять из нескольких вложенных циклов for и (или) содержать оператор ветвления if после цикла. Для примера получим четные элементы списка и умножим их на 10: arr = [1, 2, 3, 4] arr = [ i * 10 for i in arr if i % 2 == 0 ] print(arr) # Результат выполнения: [20, 40] Этот код эквивалентен коду: arr = [] for i in [1, 2, 3, 4]: if i % 2 == 0: # Если число четное arr.append(i * 10) # Добавляем элемент print(arr) # Результат выполнения: [20, 40] Усложним наш пример — получим четные элементы вложенного списка и умножим их на 10: arr = [[1, 2], [3, 4], [5, 6]] arr = [ j * 10 for i in arr for j in i if j % 2 == 0 ] print(arr) # Результат выполнения: [20, 40, 60] Этот код эквивалентен коду: arr = [] for i in [[1, 2], [3, 4], [5, 6]]: for j in i: if j % 2 == 0: # Если число четное arr.append(j * 10) # Добавляем элемент print(arr) # Результат выполнения: [20, 40, 60] Если выражение разместить внутри не квадратных, а круглых скобок, то будет возвращать- ся не список, а итератор. Такие конструкции называются выражениями-генераторами. В качестве примера просуммируем четные числа в списке: >>> arr = [1, 4, 12, 45, 10] >>> sum((i for i in arr if i % 2 == 0)) 26 8.6. Функции map(), zip(), filter() и reduce() Встроенная функция map() позволяет применить заданную в параметре функцию к каждому элементу последовательности. Она имеет такой формат: map(<Функция>, <Последовательность1>[, ..., <ПоследовательностьN>]) Функция map() возвращает объект, поддерживающий итерации (а не список, как это было ранее в Python 2). Чтобы получить список в версии Python 3, возвращенный результат необ- ходимо передать в функцию list() Глава 8. Списки, кортежи, множества и диапазоны 151 В качестве параметра <Функция> указывается ссылка на функцию (название функции без круглых скобок), которой будет передаваться текущий элемент последовательности. Внут- ри этой функции необходимо вернуть новое значение. Для примера прибавим к каждому элементу списка число 10 (листинг 8.1). Листинг 8.1. Функция map() def func(elem): """ Увеличение значения каждого элемента списка """ return elem + 10 # Возвращаем новое значение arr = [1, 2, 3, 4, 5] print( list( map(func, arr) ) ) # Результат выполнения: [11, 12, 13, 14, 15] Функции map() можно передать несколько последовательностей. В этом случае в функцию обратного вызова будут передаваться сразу несколько элементов, расположенных в после- довательностях на одинаковом смещении. Просуммируем элементы трех списков (лис- тинг 8.2). Листинг 8.2. Суммирование элементов трех списков def func(e1, e2, e3): """ Суммирование элементов трех разных списков """ return e1 + e2 + e3 # Возвращаем новое значение arr1 = [1, 2, 3, 4, 5] arr2 = [10, 20, 30, 40, 50] arr3 = [100, 200, 300, 400, 500] print( list( map(func, arr1, arr2, arr3) ) ) # Результат выполнения: [111, 222, 333, 444, 555] Если количество элементов в последовательностях различается, за основу выбирается по- следовательность с минимальным количеством элементов (листинг 8.3). Листинг 8.3. Суммирование элементов трех списков разной длины def func(e1, e2, e3): """ Суммирование элементов трех разных списков """ return e1 + e2 + e3 arr1 = [1, 2, 3, 4, 5] arr2 = [10, 20] arr3 = [100, 200, 300, 400, 500] print( list( map(func, arr1, arr2, arr3) ) ) # Результат выполнения: [111, 222] Встроенная функция zip() на каждой итерации возвращает кортеж, содержащий элементы последовательностей, которые расположены на одинаковом смещении. Функция возвраща- 152 Часть I. Основы языка Python ет объект, поддерживающий итерации (а не список, как это было ранее в Python 2). Чтобы получить список в версии Python 3, необходимо результат передать в функцию list() Формат функции: zip(<Последовательность1>[, ..., <ПоследовательностьN>]) Пример: >>> zip([1, 2, 3], [4, 5, 6], [7, 8, 9]) >>> list(zip([1, 2, 3], [4, 5, 6], [7, 8, 9])) [(1, 4, 7), (2, 5, 8), (3, 6, 9)] Если количество элементов в последовательностях будет разным, то в результат попадут только элементы, которые существуют во всех последовательностях на одинаковом смеще- нии: >>> list(zip([1, 2, 3], [4, 6], [7, 8, 9, 10])) [(1, 4, 7), (2, 6, 8)] В качестве еще одного примера переделаем нашу программу суммирования элементов трех списков (см. листинг 8.3) и используем функцию zip() вместо функции map() (листинг 8.4). Листинг 8.4. Суммирование элементов трех списков с помощью функции zip() arr1 = [1, 2, 3, 4, 5] arr2 = [10, 20, 30, 40, 50] arr3 = [100, 200, 300, 400, 500] arr = [x + y + z for (x, y, z) in zip(arr1, arr2, arr3)] print(arr) # Результат выполнения: [111, 222, 333, 444, 555] Функция filter() позволяет выполнить проверку элементов последовательности. Формат функции: filter(<Функция>, <Последовательность>) Если в первом параметре вместо названия функции указать значение None , то каждый эле- мент последовательности будет проверен на соответствие значению True . Если элемент в логическом контексте возвращает значение False , то он не будет добавлен в возвращаемый результат. Функция возвращает объект, поддерживающий итерации (а не список или кор- теж, как это было ранее в Python 2). Чтобы получить список в версии Python 3, необходимо результат передать в функцию list() : >>> filter(None, [1, 0, None, [], 2]) >>> list(filter(None, [1, 0, None, [], 2])) [1, 2] Аналогичная операция с использованием генераторов списков выглядит так: >>> [ i for i in [1, 0, None, [], 2] if i ] [1, 2] В первом параметре можно указать ссылку на функцию, в которую в качестве параметра будет передаваться текущий элемент последовательности. Если элемент нужно добавить Глава 8. Списки, кортежи, множества и диапазоны 153 в возвращаемое функцией filter() значение, то внутри функции, указанной в качестве первого параметра, следует вернуть значение True , в противном случае — значение False Для примера удалим все отрицательные значения из списка (листинг 8.5). Листинг 8.5. Пример использования функции filter() def func(elem): return elem >= 0 arr = [-1, 2, -3, 4, 0, -20, 10] arr = list(filter(func, arr)) print(arr) # Результат: [2, 4, 0, 10] # Использование генераторов списков arr = [-1, 2, -3, 4, 0, -20, 10] arr = [ i for i in arr if func(i) ] print(arr) # Результат: [2, 4, 0, 10] Функция reduce() из модуля functools применяет указанную функцию к парам элементов и накапливает результат. Функция имеет следующий формат: reduce(<Функция>, <Последовательность>[, <Начальное значение>]) В параметр <Функция> в качестве параметров передаются два элемента: первый элемент будет содержать результат предыдущих вычислений, а второй — значение текущего эле- мента. Получим сумму всех элементов списка (листинг 8.6). Листинг 8.6. Пример использования функции reduce() from functools import reduce # Подключаем модуль def func(x, y): print("({0}, {1})".format(x, y), end=" ") return x + y arr = [1, 2, 3, 4, 5] summa = reduce(func, arr) # Последовательность: (1, 2) (3, 3) (6, 4) (10, 5) print(summa) # Результат выполнения: 15 summa = reduce(func, arr, 10) # Последовательность: (10, 1) (11, 2) (13, 3) (16, 4) (20, 5) print(summa) # Результат выполнения: 25 summa = reduce(func, [], 10) print(summa) # Результат выполнения: 10 8.7. Добавление и удаление элементов списка Для добавления и удаления элементов списка используются следующие методы: append(<Объект>) — добавляет один объект в конец списка. Метод изменяет текущий список и ничего не возвращает: 154 Часть I. Основы языка Python >>> arr = [1, 2, 3] >>> arr.append(4); arr # Добавляем число [1, 2, 3, 4] >>> arr.append([5, 6]); arr # Добавляем список [1, 2, 3, 4, [5, 6]] >>> arr.append((7, 8)); arr # Добавляем кортеж [1, 2, 3, 4, [5, 6], (7, 8)] extend(<Последовательность>) — добавляет элементы последовательности в конец списка. Метод изменяет текущий список и ничего не возвращает: >>> arr = [1, 2, 3] >>> arr.extend([4, 5, 6]) # Добавляем список >>> arr.extend((7, 8, 9)) # Добавляем кортеж >>> arr.extend("abc") # Добавляем буквы из строки >>> arr [1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b', 'c'] Добавить несколько элементов можно с помощью операции конкатенации или операто- ра += : >>> arr = [1, 2, 3] >>> arr + [4, 5, 6] # Возвращает новый список [1, 2, 3, 4, 5, 6] >>> arr += [4, 5, 6] # Изменяет текущий список >>> arr [1, 2, 3, 4, 5, 6] Кроме того, можно воспользоваться операцией присваивания значения срезу: >>> arr = [1, 2, 3] >>> arr[len(arr):] = [4, 5, 6] # Изменяет текущий список >>> arr [1, 2, 3, 4, 5, 6] insert(<Индекс>, <Объект>) — добавляет один объект в указанную позицию. Осталь- ные элементы смещаются. Метод изменяет текущий список и ничего не возвращает: >>> arr = [1, 2, 3] >>> arr.insert(0, 0); arr # Вставляем 0 в начало списка [0, 1, 2, 3] >>> arr.insert(-1, 20); arr # Можно указать отрицательные числа [0, 1, 2, 20, 3] >>> arr.insert(2, 100); arr # Вставляем 100 в позицию 2 [0, 1, 100, 2, 20, 3] >>> arr.insert(10, [4, 5]); arr # Добавляем список [0, 1, 100, 2, 20, 3, [4, 5]] Метод insert() позволяет добавить только один объект. Чтобы добавить несколько объектов, можно воспользоваться операцией присваивания значения срезу. Добавим несколько элементов в начало списка: >>> arr = [1, 2, 3] >>> arr[:0] = [-2, -1, 0] >>> arr [-2, -1, 0, 1, 2, 3] Глава 8. Списки, кортежи, множества и диапазоны 155 pop([<Индекс>]) — удаляет элемент, расположенный по указанному индексу, и воз- вращает его. Если индекс не указан, то удаляет и возвращает последний элемент списка. Если элемента с указанным индексом нет, или список пустой, возбуждается исключение IndexError : >>> arr = [1, 2, 3, 4, 5] >>> arr.pop() # Удаляем последний элемент списка 5 >>> arr # Список изменился [1, 2, 3, 4] >>> arr.pop(0) # Удаляем первый элемент списка 1 >>> arr # Список изменился [2, 3, 4] Удалить элемент списка позволяет также оператор del : >>> arr = [1, 2, 3, 4, 5] >>> del arr[4]; arr # Удаляем последний элемент списка [1, 2, 3, 4] >>> del arr[:2]; arr # Удаляем первый и второй элементы [3, 4] remove(<Значение>) — удаляет первый элемент, содержащий указанное значение. Если элемент не найден, возбуждается исключение ValueError . Метод изменяет текущий спи- сок и ничего не возвращает: >>> arr = [1, 2, 3, 1, 1] >>> arr.remove(1) # Удаляет только первый элемент >>> arr [2, 3, 1, 1] >>> arr.remove(5) # Такого элемента нет Traceback (most recent call last): File " ", line 1, in ValueError: list.remove(x): x not in list clear() — удаляет все элементы списка, очищая его. Никакого результата при этом не возвращается: >>> arr = [1, 2, 3, 1, 1] >>> arr.clear() >>> arr [] Если необходимо удалить все повторяющиеся элементы списка, то можно преобразовать список во множество, а затем множество обратно преобразовать в список. Обратите внима- ние на то, что список должен содержать только неизменяемые объекты (например, числа, строки или кортежи). В противном случае возбуждается исключение TypeError : >>> arr = [1, 2, 3, 1, 1, 2, 2, 3, 3] >>> s = set(arr) # Преобразуем список во множество >>> s {1, 2, 3} >>> arr = list(s) # Преобразуем множество в список >>> arr # Все повторы были удалены [1, 2, 3] 156 Часть I. Основы языка Python 8.8. Поиск элемента в списке и получение сведений о значениях, входящих в список Как вы уже знаете, выполнить проверку на вхождение элемента в список позволяет опера- тор in : если элемент входит в список, то возвращается значение True , в противном слу- чае — False . Аналогичный оператор not in выполняет проверку на невхождение элемента в список: если элемент отсутствует в списке, возвращается True , в противном случае — False : >>> 2 in [1, 2, 3, 4, 5], 6 in [1, 2, 3, 4, 5] # Проверка на вхождение (True, False) >>> 2 not in [1, 2, 3, 4, 5], 6 not in [1, 2, 3, 4, 5] # Проверка на невхождение (False, True) Тем не менее оба этих оператора не дают никакой информации о местонахождении элемен- та внутри списка. Чтобы узнать индекс элемента внутри списка, следует воспользоваться методом index() . Формат метода: index(<Значение>[, <Начало>[, <Конец>]]) Метод index() возвращает индекс элемента, имеющего указанное значение. Если значение не входит в список, то возбуждается исключение ValueError . Если второй и третий пара- метры не указаны, поиск будет производиться с начала и до конца списка: >>> arr = [1, 2, 1, 2, 1] >>> arr.index(1), arr.index(2) (0, 1) >>> arr.index(1, 1), arr.index(1, 3, 5) (2, 4) >>> arr.index(3) Traceback (most recent call last): File " ", line 1, in ValueError: 3 is not in list Узнать общее количество элементов с указанным значением позволяет метод count(<Значение>) . Если элемент не входит в список, возвращается значение 0 : >>> arr = [1, 2, 1, 2, 1] >>> arr.count(1), arr.count(2) (3, 2) >>> arr.count(3) # Элемент не входит в список 0 С помощью функций max() и min() можно узнать максимальное и минимальное значение из всех, что входят в список, соответственно: >>> arr = [1, 2, 3, 4, 5] >>> max(arr), min(arr) (5, 1) Функция any(<Последовательность>) возвращает значение True , если в последовательности существует хотя бы один элемент, который в логическом контексте возвращает значение True . Если последовательность не содержит элементов, возвращается значение False : >>> any([0, None]), any([0, None, 1]), any([]) (False, True, False) Глава 8. Списки, кортежи, множества и диапазоны 157 Функция all(<Последовательность>) возвращает значение True , если все элементы после- довательности в логическом контексте возвращают значение True или последовательность не содержит элементов: >>> all([0, None]), all([0, None, 1]), all([]), all(["str", 10]) (False, False, True, True) 8.9. Переворачивание и перемешивание списка Метод reverse() изменяет порядок следования элементов списка на противоположный. Метод изменяет текущий список и ничего не возвращает: >>> arr = [1, 2, 3, 4, 5] >>> arr.reverse() # Изменяется текущий список >>> arr [5, 4, 3, 2, 1] Если необходимо изменить порядок следования и получить новый список, следует восполь- зоваться функцией reversed(<Последовательность>) . Она возвращает итератор, который можно преобразовать в список с помощью функции list() или генератора списков: >>> arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] >>> reversed(arr) >>> list(reversed(arr)) # Использование функции list() [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] >>> for i in reversed(arr): print(i, end=" ") # Вывод с помощью цикла 10 9 8 7 6 5 4 3 2 1 >>> [i for i in reversed(arr)] # Использование генератора списков [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] Функция shuffle(<Список>[, <Число от 0.0 до 1.0>]) из модуля random перемешивает список случайным образом. Функция перемешивает сам список и ничего не возвращает. Если второй параметр не указан, используется значение, возвращаемое функцией random() : >>> import random # Подключаем модуль random >>> arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] >>> random.shuffle(arr) # Перемешиваем список случайным образом >>> arr [2, 7, 10, 4, 6, 8, 9, 3, 1, 5] 8.10. Выбор элементов случайным образом Получить элементы из списка случайным образом позволяют следующие функции из моду- ля random : choice(<Последовательность>) — возвращает случайный элемент из любой последова- тельности (строки, списка, кортежа): >>> import random # Подключаем модуль random >>> random.choice(["s", "t", "r"]) # Список 's' 158 Часть I. Основы языка Python sample(<Последовательность>, <Количество элементов>) — возвращает список из ука- занного количества элементов. В этот список попадут элементы из последовательности, выбранные случайным образом. В качестве последовательности можно указать любые объекты, поддерживающие итерации: >>> arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] >>> random.sample(arr, 2) [7, 10] >>> arr # Сам список не изменяется [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 8.11. Сортировка списка Отсортировать список позволяет метод sort() . Он имеет следующий формат: sort([key=None][, reverse=False]) Все параметры не являются обязательными. Метод изменяет текущий список и ничего не возвращает. Отсортируем список по возрастанию с параметрами по умолчанию: >>> arr = [2, 7, 10, 4, 6, 8, 9, 3, 1, 5] >>> arr.sort() # Изменяет текущий список >>> arr [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] Чтобы отсортировать список по убыванию, следует в параметре reverse указать значение True : >>> arr = [2, 7, 10, 4, 6, 8, 9, 3, 1, 5] >>> arr.sort(reverse=True) # Сортировка по убыванию >>> arr [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] Надо заметить, что стандартная сортировка зависит от регистра символов (листинг 8.7). Листинг 8.7. Стандартная сортировка arr = ["единица1", "Единый", "Единица2"] arr.sort() for i in arr: print(i, end=" ") # Результат выполнения: Единица2 Единый единица1 В параметре key метода sort() можно указать функцию, выполняющую какое-либо дейст- вие над каждым элементом списка. В качестве единственного параметра она должна при- нимать значение очередного элемента списка, а в качестве результата — возвращать результат действий над ним. Этот результат будет участвовать в процессе сортировки, но значения самих элементов списка не изменятся. Выполнив пример из листинга 8.7, мы получили неправильный результат сортировки, ведь Единый и Единица2 больше единица1 . Чтобы регистр символов не учитывался, в параметре key мы укажем функцию для изменения регистра символов (листинг 8.8). Глава 8. Списки, кортежи, множества и диапазоны 159 Листинг 8.8. Пользовательская сортировка arr = ["единица1", "Единый", "Единица2"] arr.sort(key=str.lower) # Указываем метод lower() for i in arr: print(i, end=" ") # Результат выполнения: единица1 Единица2 Единый Метод sort() сортирует сам список и не возвращает никакого значения. В некоторых случаях необходимо получить отсортированный список, а текущий список оставить без изменений. Для этого следует воспользоваться функцией sorted() . Функция имеет сле- дующий формат: sorted(<Последовательность>[, key=None][, reverse=False]) В первом параметре указывается список, который необходимо отсортировать. Остальные параметры эквивалентны параметрам метода sort() . Вот пример использования функции sorted() : >>> arr = [2, 7, 10, 4, 6, 8, 9, 3, 1, 5] >>> sorted(arr) # Возвращает новый список! [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] >>> sorted(arr, reverse=True) # Возвращает новый список! [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] >>> arr = ["единица1", "Единый", "Единица2"] >>> sorted(arr, key=str.lower) ['единица1', 'Единица2', 'Единый'] 8.12. Заполнение списка числами Создать список, содержащий диапазон чисел, можно с помощью функции range() . Она воз- вращает диапазон, который преобразуется в список вызовом функции list() . Функция range() имеет следующий формат: range([<Начало>, ]<Конец>[, <Шаг>]) Первый параметр задает начальное значение, а если он не указан, используется значение 0 Во втором параметре указывается конечное значение. Следует заметить, что это значение не входит в возвращаемый диапазон. Если параметр <Шаг> не указан, используется значение 1 . В качестве примера заполним список числами от 0 до 10 : >>> list(range(11)) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] Создадим список, состоящий из диапазона чисел от 1 до 15 : >>> list(range(1, 16)) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] Теперь изменим порядок следования чисел на противоположный: >>> list(range(15, 0, -1)) [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1] Если необходимо получить список со случайными числами (или случайными элементами из другого списка), то следует воспользоваться функцией sample(<Последовательность>, <Количество элементов>) из модуля random : 160 Часть I. Основы языка Python >>> import random >>> arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] >>> random.sample(arr, 3) [1, 9, 5] >>> random.sample(range(300), 5) [259, 294, 142, 292, 245] 8.13. Преобразование списка в строку Преобразовать список в строку позволяет метод join() . Элементы добавляются в форми- руемую строку через указанный разделитель. Формат метода: <Строка> = <Разделитель>.join(<Последовательность>) Пример: >>> arr = ["word1", "word2", "word3"] >>> " — ".join(arr) 'word1 — word2 — word3' Обратите внимание на то, что элементы списка должны быть строками, иначе возвращается исключение TypeError : >>> arr = ["word1", "word2", "word3", 2] >>> " — ".join(arr) Traceback (most recent call last): File " ", line 1, in " — ".join(arr) TypeError: sequence item 3: expected str instance, int found Избежать этого исключения можно с помощью выражения-генератора, внутри которого текущий элемент списка преобразуется в строку с помощью функции str() : >>> arr = ["word1", "word2", "word3", 2] >>> " — ".join( ( str(i) for i in arr ) ) 'word1 — word2 — word3 — 2' Кроме того, с помощью функции str() можно сразу получить строковое представление списка: >>> arr = ["word1", "word2", "word3", 2] >>> str(arr) "['word1', 'word2', 'word3', 2]" 8.14. Кортежи Кортежи, как и списки, являются упорядоченными последовательностями элементов. Они во многом аналогичны спискам, но имеют одно очень важное отличие — изменить кортеж нельзя. Можно сказать, что кортеж — это список, доступный только для чтения. Создать кортеж можно следующими способами: с помощью функции tuple([<Последовательность>]) . Функция позволяет преобразовать любую последовательность в кортеж. Если параметр не указан, создается пустой кортеж: Глава 8. Списки, кортежи, множества и диапазоны 161 >>> tuple() # Создаем пустой кортеж () >>> tuple("String") # Преобразуем строку в кортеж ('S', 't', 'r', 'i', 'n', 'g') >>> tuple([1, 2, 3, 4, 5]) # Преобразуем список в кортеж (1, 2, 3, 4, 5) указав все элементы через запятую внутри круглых скобок (или без скобок): >>> t1 = () # Создаем пустой кортеж >>> t2 = (5,) # Создаем кортеж из одного элемента >>> t3 = (1, "str", (3, 4)) # Кортеж из трех элементов >>> t4 = 1, "str", (3, 4) # Кортеж из трех элементов >>> t1, t2, t3, t4 ((), (5,), (1, 'str', (3, 4)), (1, 'str', (3, 4))) Обратите особое внимание на вторую строку примера. Чтобы создать кортеж из одного элемента, необходимо в конце указать запятую. Именно запятые формируют кортеж, а не круглые скобки. Если внутри круглых скобок нет запятых, будет создан объект дру- гого типа: >>> t = (5); type(t) # Получили число, а не кортеж! >>> t = ("str"); type(t) # Получили строку, а не кортеж! Четвертая строка в исходном примере также доказывает, что не скобки формируют кор- теж, а запятые. Помните, что любое выражение в языке Python можно заключить в круг- лые скобки, а чтобы получить кортеж, необходимо указать запятые. Позиция элемента в кортеже задается индексом. Обратите внимание на то, что нумерация элементов кортежа (как и списка) начинается с 0 , а не с 1 . Как и все последовательности, кортежи поддерживают обращение к элементу по индексу, получение среза, конкатенацию (оператор + ), повторение (оператор * ), проверку на вхождение (оператор in ) и невхождение (оператор not in ): >>> t = (1, 2, 3, 4, 5, 6, 7, 8, 9) >>> t[0] # Получаем значение первого элемента кортежа 1 >>> t[::-1] # Изменяем порядок следования элементов (9, 8, 7, 6, 5, 4, 3, 2, 1) >>> t[2:5] # Получаем срез (3, 4, 5) >>> 8 in t, 0 in t # Проверка на вхождение (True, False) >>> (1, 2, 3) * 3 # Повторение (1, 2, 3, 1, 2, 3, 1, 2, 3) >>> (1, 2, 3) + (4, 5, 6) # Конкатенация (1, 2, 3, 4, 5, 6) Кортежи, как уже неоднократно отмечалось, относятся к неизменяемым типам данных. Иными словами, можно получить элемент по индексу, но изменить его нельзя: >>> t = (1, 2, 3) # Создаем кортеж >>> t[0] # Получаем элемент по индексу 1 162 Часть I. Основы языка Python >>> t[0] = 50 # Изменить элемент по индексу нельзя! Traceback (most recent call last): File " ", line 1, in TypeError: 'tuple' object does not support item assignment Кортежи поддерживают уже знакомые нам по спискам функции len() , min() , max() , методы index() и count() : >>> t = (1, 2, 3) # Создаем кортеж >>> len(t) # Получаем количество элементов 3 >>> t = (1, 2, 1, 2, 1) >>> t.index(1), t.index(2) # Ищем элементы в кортеже (0, 1) 8.15. Множества Множество — это неупорядоченная последовательность уникальных элементов. Объявить множество можно с помощью функции set() : >>> s = set() >>> s set([]) Функция set() также позволяет преобразовать элементы последовательности во множество: >>> set("string") # Преобразуем строку set(['g', 'i', 'n', 's', 'r', 't']) >>> set([1, 2, 3, 4, 5]) # Преобразуем список set([1, 2, 3, 4, 5]) >>> set((1, 2, 3, 4, 5)) # Преобразуем кортеж set([1, 2, 3, 4, 5]) >>> set([1, 2, 3, 1, 2, 3]) # Остаются только уникальные элементы set([1, 2, 3]) Перебрать элементы множества позволяет цикл for : >>> for i in set([1, 2, 3]): print i 1 2 3 Получить количество элементов множества позволяет функция len() : >>> len(set([1, 2, 3])) 3 Для работы с множествами предназначены следующие операторы и соответствующие им методы: | и union() — объединяют два множества: >>> s = set([1, 2, 3]) >>> s.union(set([4, 5, 6])), s | set([4, 5, 6]) (set([1, 2, 3, 4, 5, 6]), set([1, 2, 3, 4, 5, 6])) Если элемент уже содержится во множестве, то он повторно добавлен не будет: >>> set([1, 2, 3]) | set([1, 2, 3]) set([1, 2, 3]) Глава 8. Списки, кортежи, множества и диапазоны 163 a |= b и a.update(b) — добавляют элементы множества b во множество a : >>> s = set([1, 2, 3]) >>> s.update(set([4, 5, 6])) >>> s set([1, 2, 3, 4, 5, 6]) >>> s |= set([7, 8, 9]) >>> s set([1, 2, 3, 4, 5, 6, 7, 8, 9]) - и difference() — вычисляют разницу множеств: >>> set([1, 2, 3]) - set([1, 2, 4]) set([3]) >>> s = set([1, 2, 3]) >>> s.difference(set([1, 2, 4])) set([3]) a -= b и a.difference_update(b) — удаляют элементы из множества a , которые сущест- вуют и во множестве a , и во множестве b : >>> s = set([1, 2, 3]) >>> s.difference_update(set([1, 2, 4])) >>> s set([3]) >>> s -= set([3, 4, 5]) >>> s set([]) & и intersection() — пересечение множеств. Позволяют получить элементы, которые существуют в обоих множествах: >>> set([1, 2, 3]) & set([1, 2, 4]) set([1, 2]) >>> s = set([1, 2, 3]) >>> s.intersection(set([1, 2, 4])) set([1, 2]) a &= b и a.intersection_update(b) — во множестве a останутся элементы, которые су- ществуют и во множестве a , и во множестве b : >>> s = set([1, 2, 3]) >>> s.intersection_update(set([1, 2, 4])) >>> s set([1, 2]) >>> s &= set([1, 6, 7]) >>> s set([1]) ^ и symmetric_difference() — возвращают все элементы обоих множеств, исключая элементы, которые присутствуют в обоих этих множествах: >>> s = set([1, 2, 3]) >>> s ^ set([1, 2, 4]), s.symmetric_difference(set([1, 2, 4])) (set([3, 4]), set([3, 4])) >>> s ^ set([1, 2, 3]), s.symmetric_difference(set([1, 2, 3])) (set([]), set([])) 164 Часть I. Основы языка Python >>> s ^ set([4, 5, 6]), s.symmetric_difference(set([4, 5, 6])) (set([1, 2, 3, 4, 5, 6]), set([1, 2, 3, 4, 5, 6])) a ^= b и a.symmetric_difference_update(b) — во множестве a будут все элементы обо- их множеств, исключая те, что присутствуют в обоих этих множествах: >>> s = set([1, 2, 3]) >>> s.symmetric_difference_update(set([1, 2, 4])) >>> s set([3, 4]) >>> s ^= set([3, 5, 6]) >>> s set([4, 5, 6]) Операторы сравнения множеств: in — проверка наличия элемента во множестве: >>> s = set([1, 2, 3, 4, 5]) >>> 1 in s, 12 in s (True, False) not in — проверка отсутствия элемента во множестве: >>> s = set([1, 2, 3, 4, 5]) >>> 1 in s, 12 in s (False, True) == — проверка на равенство: >>> set([1, 2, 3]) == set([1, 2, 3]) True >>> set([1, 2, 3]) == set([3, 2, 1]) True >>> set([1, 2, 3]) == set([1, 2, 3, 4]) False a <= b и a.issubset(b) — проверяют, входят ли все элементы множества a во множест- во b : >>> s = set([1, 2, 3]) >>> s <= set([1, 2]), s <= set([1, 2, 3, 4]) (False, True) >>> s.issubset(set([1, 2])), s.issubset(set([1, 2, 3, 4])) (False, True) a < b — проверяет, входят ли все элементы множества a во множество b , причем мно- жество a не должно быть равно множеству b : >>> s = set([1, 2, 3]) >>> s < set([1, 2, 3]), s < set([1, 2, 3, 4]) (False, True) a >= b и a.issuperset(b) — проверяют, входят ли все элементы множества b во множе- ство a : >>> s = set([1, 2, 3]) >>> s >= set([1, 2]), s >= set([1, 2, 3, 4]) (True, False) Глава 8. Списки, кортежи, множества и диапазоны 165 >>> s.issuperset(set([1, 2])), s.issuperset(set([1, 2, 3, 4])) (True, False) a > b — проверяет, входят ли все элементы множества b во множество a , причем мно- жество a не должно быть равно множеству b : >>> s = set([1, 2, 3]) >>> s > set([1, 2]), s > set([1, 2, 3]) (True, False) a.isdisjoint(b) — проверяет, являются ли множества a и b полностью разными, т. е. не содержащими ни одного совпадающего элемента: >>> s = set([1, 2, 3]) >>> s.isdisjoint(set([4, 5, 6])) True >>> s.isdisjoint(set([1, 3, 5])) False Для работы с множествами предназначены следующие методы: copy() — создает копию множества. Обратите внимание на то, что оператор = присваи- вает лишь ссылку на тот же объект, а не копирует его: >>> s = set([1, 2, 3]) >>> c = s; s is c # С помощью = копию создать нельзя! True >>> c = s.copy() # Создаем копию объекта >>> c set([1, 2, 3]) >>> s is c # Теперь это разные объекты False add(<Элемент>) — добавляет <Элемент> во множество: >>> s = set([1, 2, 3]) >>> s.add(4); s set([1, 2, 3, 4]) remove(<Элемент>) — удаляет <Элемент> из множества. Если элемент не найден, то воз- буждается исключение KeyError : >>> s = set([1, 2, 3]) >>> s.remove(3); s # Элемент существует set([1, 2]) >>> s.remove(5) # Элемент НЕ существует Traceback (most recent call last): File " ", line 1, in KeyError: 5 discard(<Элемент>) — удаляет <Элемент> из множества, если он присутствует. Если ука- занный элемент не существует, никакого исключения не возбуждается: >>> s = set([1, 2, 3]) >>> s.discard(3); s # Элемент существует set([1, 2]) 166 Часть I. Основы языка Python >>> s.discard(5); s # Элемент НЕ существует set([1, 2]) pop() — удаляет произвольный элемент из множества и возвращает его. Если элементов нет, возбуждается исключение KeyError : >>> s = set([1, 2]) >>> s.pop(), s (1, set([2])) >>> s.pop(), s (2, set([])) >>> s.pop() # Если нет элементов, то будет ошибка Traceback (most recent call last): File " ", line 1, in KeyError: 'pop from an empty set' clear() — удаляет все элементы из множества: >>> s = set([1, 2, 3]) >>> s.clear(); s set([]) Помимо генераторов списков и генераторов словарей, язык Python 3 поддерживает генера- торы множеств. Их синтаксис похож на синтаксис генераторов списков, но выражение заключается в фигурные скобки, а не в квадратные. Так как результатом является множест- во, все повторяющиеся элементы будут удалены: >>> {x for x in [1, 2, 1, 2, 1, 2, 3]} {1, 2, 3} Генераторы множеств могут иметь сложную структуру. Например, состоять из нескольких вложенных циклов for и (или) содержать оператор ветвления if после цикла. Создадим из элементов исходного списка множество, содержащее только уникальные элементы с чет- ными значениями: >>> {x for x in [1, 2, 1, 2, 1, 2, 3] if x % 2 == 0} {2} Язык Python поддерживает еще один тип множеств — frozenset . В отличие от типа set , множество типа frozenset нельзя изменить. Объявить такое множество можно с помощью функции frozenset() : >>> f = frozenset() >>> f frozenset([]) Функция frozenset() позволяет также преобразовать элементы последовательности во множество: >>> frozenset("string") # Преобразуем строку frozenset(['g', 'i', 'n', 's', 'r', 't']) >>> frozenset([1, 2, 3, 4, 4]) # Преобразуем список frozenset([1, 2, 3, 4]) >>> frozenset((1, 2, 3, 4, 4)) # Преобразуем кортеж frozenset([1, 2, 3, 4]) Глава 8. Списки, кортежи, множества и диапазоны 167 Множества frozenset поддерживают операторы, которые не изменяют само множество, а также следующие методы: copy() , difference() , intersection() , issubset() , issuperset() , symmetric_difference() и union() 8.16. Диапазоны Диапазоны, как следует из самого их названия, — это последовательности целых чисел с заданными начальным и конечным значениями и шагом (промежутком между соседними числами). Как и списки, кортежи и множества, диапазоны представляют собой последова- тельности и, подобно кортежам, являются неизменяемыми. Важнейшим преимуществом диапазонов перед другими видами последовательностей явля- ется их компактность — вне зависимости от количества входящих в него элементов-чисел, диапазон всегда занимает один и тот же объем оперативной памяти. Однако в диапазон могут входить лишь числа, последовательно стоящие друг за другом, — сформировать диа- пазон на основе произвольного набора чисел или данных другого типа, даже чисел с пла- вающей точкой, невозможно. Диапазоны чаще всего используются для проверки вхождения значения в какой-либо ин- тервал и для организации циклов. Для создания диапазона применяется функция range() : range([<Начало>, ]<Конец>[, <Шаг>]) Первый параметр задает начальное значение — если он не указан, используется значение 0 Во втором параметре указывается конечное значение. Следует заметить, что это значение не входит в возвращаемый диапазон. Если параметр <Шаг> не указан, используется значение 1 : >>> r = range(1, 10) >>> for i in r: print(i, end = " ") 1 2 3 4 5 6 7 8 9 >>> r = range(10, 110, 10) >>> for i in r: print(i, end = " ") 10 20 30 40 50 60 70 80 90 100 >>> r = range(10, 1, -1) >>> for i in r: print(i, end = " ") 10 9 8 7 6 5 4 3 2 Преобразовать диапазон в список, кортеж, обычное или неизменяемое множество можно с помощью функций list() , tuple() , set() или frozenset() соответственно: >>> list(range(1, 10)) # Преобразуем в список [1, 2, 3, 4, 5, 6, 7, 8, 9] >>> tuple(range(1, 10)) # Преобразуем в кортеж (1, 2, 3, 4, 5, 6, 7, 8, 9) >>> set(range(1, 10)) # Преобразуем в множество {1, 2, 3, 4, 5, 6, 7, 8, 9} Множества поддерживают доступ к элементу по индексу, получение среза (в результате возвращается также диапазон), проверку на вхождение и невхождение, функции len() , min() , max() , методы index() и count() : >>> r = range(1, 10) >>> r[2], r[-1] (3, 9) 168 Часть I. Основы языка Python >>> r[2:4] range(3, 5) >>> 2 in r, 12 in r (True, False) >>> 3 not in r, 13 not in r (False, True) >>> len(r), min(r), max(r) (9, 1, 9) >>> r.index(4), r.count(4) (3, 1) Поддерживается ряд операторов, позволяющих сравнить два диапазона: == — возвращает True , если диапазоны равны, и False — в противном случае. Диапазо- ны считаются равными, если они содержат одинаковые последовательности чисел: >>> range(1, 10) == range(1, 10, 1) True >>> range(1, 10, 2) == range(1, 11, 2) True >>> range(1, 10, 2) == range(1, 12, 2) False != — возвращает True , если диапазоны не равны, и False — в противном случае: >>> range(1, 10, 2) != range(1, 12, 2) True >>> range(1, 10) != range(1, 10, 1) False Также диапазоны поддерживают атрибуты start , stop и step , возвращающие, соответст- венно, начальную, конечную границы диапазона и его шаг: >>> r = range(1, 10) >>> r.start, r.stop, r.step (1, 10, 1) 8.17. Модуль itertools Модуль itertools содержит функции, позволяющие генерировать различные последова- тельности на основе других последовательностей, производить фильтрацию элементов и др. Все функции возвращают объекты, поддерживающие итерации. Прежде чем использовать функции, необходимо подключить модуль с помощью инструкции: import itertools 8.17.1. Генерирование неопределенного количества значений Для генерации неопределенного количества значений предназначены следующие функции: count([start=0][, step=1]) — создает бесконечно нарастающую последовательность значений. Начальное значение задается параметром start , а шаг — параметром step : >>> import itertools >>> for i in itertools.count(): Глава 8. Списки, кортежи, множества и диапазоны 169 if i > 10: break print(i, end=" ") 0 1 2 3 4 5 6 7 8 9 10 >>> list(zip(itertools.count(), "абвгд")) [(0, 'а'), (1, 'б'), (2, 'в'), (3, 'г'), (4, 'д')] >>> list(zip(itertools.count(start=2, step=2), "абвгд")) [(2, 'а'), (4, 'б'), (6, 'в'), (8, 'г'), (10, 'д')] cycle(<Последовательность>) — на каждой итерации возвращает очередной элемент последовательности. Когда будет достигнут конец последовательности, перебор начнет- ся сначала, и так до бесконечности: >>> n = 1 >>> for i in itertools.cycle("абв"): if n > 10: break print(i, end=" ") n += 1 а б в а б в а б в а >>> list(zip(itertools.cycle([0, 1]), "абвгд")) [(0, 'а'), (1, 'б'), (0, 'в'), (1, 'г'), (0, 'д')] repeat(<Объект>[, <Количество повторов>]) — возвращает объект указанное количест- во раз. Если количество повторов не указано, объект возвращается бесконечно: >>> list(itertools.repeat(1, 10)) [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] >>> list(zip(itertools.repeat(5), "абвгд")) [(5, 'а'), (5, 'б'), (5, 'в'), (5, 'г'), (5, 'д')] 8.17.2. Генерирование комбинаций значений Получить различные комбинации значений позволяют следующие функции: combinations() — на каждой итерации возвращает кортеж, содержащий комбинацию из указанного количества элементов. При этом элементы в кортеже гарантированно будут разными. Формат функции: combinations(<Последовательность>, <Количество элементов>) Примеры: >>> import itertools >>> list(itertools.combinations('абвг', 2)) [('а', 'б'), ('а', 'в'), ('а', 'г'), ('б', 'в'), ('б', 'г'), ('в', 'г')] >>> ["".join(i) for i in itertools.combinations('абвг', 2)] ['аб', 'ав', 'аг', 'бв', 'бг', 'вг'] >>> list(itertools.combinations('вгаб', 2)) [('в', 'г'), ('в', 'а'), ('в', 'б'), ('г', 'а'), ('г', 'б'), ('а', 'б')] >>> list(itertools.combinations('абвг', 3)) [('а', 'б', 'в'), ('а', 'б', 'г'), ('а', 'в', 'г'), ('б', 'в', 'г')] 170 Часть I. Основы языка Python combinations_with_replacement() — на каждой итерации возвращает кортеж, содержа- щий комбинацию из указанного количества элементов. При этом кортеж может содер- жать одинаковые элементы. Формат функции: combinations_with_replacement(<Последовательность>, <Количество элементов>) Примеры: >>> list(itertools.combinations_with_replacement('абвг', 2)) [('а', 'а'), ('а', 'б'), ('а', 'в'), ('а', 'г'), ('б', 'б'), ('б', 'в'), ('б', 'г'), ('в', 'в'), ('в', 'г'), ('г', 'г')] >>> list(itertools.combinations_with_replacement('вгаб', 2)) [('в', 'в'), ('в', 'г'), ('в', 'а'), ('в', 'б'), ('г', 'г'), ('г', 'а'), ('г', 'б'), ('а', 'а'), ('а', 'б'), ('б', 'б')] permutations() — на каждой итерации возвращает кортеж, содержащий комбинацию из указанного количества элементов. Если количество элементов не указано, то использу- ется длина последовательности. Формат функции: permutations(<Последовательность>[, <Количество элементов>]) Примеры: >>> list(itertools.permutations('абвг', 2)) [('а', 'б'), ('а', 'в'), ('а', 'г'), ('б', 'а'), ('б', 'в'), ('б', 'г'), ('в', 'а'), ('в', 'б'), ('в', 'г'), ('г', 'а'), ('г', 'б'), ('г', 'в')] >>> ["".join(i) for i in itertools.permutations('абвг')] ['абвг', 'абгв', 'авбг', 'авгб', 'агбв', 'агвб', 'бавг', 'багв', 'бваг', 'бвга', 'бгав', 'бгва', 'вабг', 'вагб', 'вбаг', 'вбга', 'вгаб', 'вгба', 'габв', 'гавб', 'гбав', 'гбва', 'гваб', 'гвба'] product() — на каждой итерации возвращает кортеж, содержащий комбинацию из эле- ментов одной или нескольких последовательностей. Формат функции: product(<Последовательность1>[,..., <ПоследовательностьN>][, repeat=1]) Примеры: >>> from itertools import product >>> list(product('абвг', repeat=2)) [('а', 'а'), ('а', 'б'), ('а', 'в'), ('а', 'г'), ('б', 'а'), ('б', 'б'), ('б', 'в'), ('б', 'г'), ('в', 'а'), ('в', 'б'), ('в', 'в'), ('в', 'г'), ('г', 'а'), ('г', 'б'), ('г', 'в'), ('г', 'г')] >>> ["".join(i) for i in product('аб', 'вг', repeat=1)] ['ав', 'аг', 'бв', 'бг'] >>> ["".join(i) for i in product('аб', 'вг', repeat=2)] ['авав', 'аваг', 'авбв', 'авбг', 'агав', 'агаг', 'агбв', 'агбг', 'бвав', 'бваг', 'бвбв', 'бвбг', 'бгав', 'бгаг', 'бгбв', 'бгбг'] Глава 8. Списки, кортежи, множества и диапазоны 171 8.17.3. Фильтрация элементов последовательности Для фильтрации элементов последовательности предназначены следующие функции: filterfalse(<Функция>, <Последовательность> ) — возвращает элементы последователь- ности (по одному на каждой итерации), для которых функция, указанная в первом пара- метре, вернет значение False : >>> import itertools >>> def func(x): return x > 3 >>> list(itertools.filterfalse(func, [4, 5, 6, 0, 7, 2, 3])) [0, 2, 3] >>> list(filter(func, [4, 5, 6, 0, 7, 2, 3])) [4, 5, 6, 7] Если в первом параметре вместо названия функции указать значение None , то каждый элемент последовательности будет проверен на соответствие значению False . Если эле- мент в логическом контексте возвращает значение True , то он не войдет в возвращаемый результат: >>> list(itertools.filterfalse(None, [0, 5, 6, 0, 7, 0, 3])) [0, 0, 0] >>> list(filter(None, [0, 5, 6, 0, 7, 0, 3])) [5, 6, 7, 3] dropwhile(<Функция>, <Последовательность>) — возвращает элементы последователь- ности (по одному на каждой итерации), начиная с элемента, для которого функция, ука- занная в первом параметре, вернет значение False : >>> def func(x): return x > 3 >>> list(itertools.dropwhile(func, [4, 5, 6, 0, 7, 2, 3])) [0, 7, 2, 3] >>> list(itertools.dropwhile(func, [4, 5, 6, 7, 8])) [] >>> list(itertools.dropwhile(func, [1, 2, 4, 5, 6, 7, 8])) [1, 2, 4, 5, 6, 7, 8] takewhile(<Функция>, <Последовательность>) — возвращает элементы последователь- ности (по одному на каждой итерации), пока не встретится элемент, для которого функ- ция, указанная в первом параметре, вернет значение False : >>> def func(x): return x > 3 >>> list(itertools.takewhile(func, [4, 5, 6, 0, 7, 2, 3])) [4, 5, 6] >>> list(itertools.takewhile(func, [4, 5, 6, 7, 8])) [4, 5, 6, 7, 8] >>> list(itertools.takewhile(func, [1, 2, 4, 5, 6, 7, 8])) [] compress() — производит фильтрацию последовательности, указанной в первом пара- метре. Элемент возвращается, только если соответствующий элемент (с таким же индек- сом) из второй последовательности трактуется как истина. Сравнение заканчивается, когда достигнут конец одной из последовательностей. Формат функции: 172 Часть I. Основы языка Python compress(<Фильтруемая последовательность>, <Последовательность логических значений>) Примеры: >>> list(itertools.compress('абвгде', [1, 0, 0, 0, 1, 1])) ['а', 'д', 'е'] >>> list(itertools.compress('абвгде', [True, False, True])) ['а', 'в'] 8.17.4. Прочие функции Помимо функций, которые мы рассмотрели в предыдущих подразделах, модуль itertools содержит несколько дополнительных функций: islice() — на каждой итерации возвращает очередной элемент последовательности. Поддерживаются форматы: islice(<Последовательность>, <Конечная граница>) и islice(<Последовательность>, <Начальная граница>, <Конечная граница>[, <Шаг>]) Если <Шаг> не указан, будет использовано значение 1 Примеры: >>> list(itertools.islice("абвгдезж", 3)) ['а', 'б', 'в'] >>> list(itertools.islice("абвгдезж", 3, 6)) ['г', 'д', 'е'] >>> list(itertools.islice("абвгдезж", 3, 6, 2)) ['г', 'е'] starmap(<Функция>, <Последовательность>) — формирует последовательность на осно- вании значений, возвращенных указанной функцией. Исходная последовательность должна содержать в качестве элементов кортежи — именно над элементами этих корте- жей функция и станет вычислять значения, которые войдут в генерируемую последова- тельность. Примеры суммирования значений: >>> import itertools >>> def func1(x, y): return x + y >>> list(itertools.starmap(func1, [(1, 2), (4, 5), (6, 7)])) [3, 9, 13] >>> def func2(x, y, z): return x + y + z >>> list(itertools.starmap(func2, [(1, 2, 3), (4, 5, 6)])) [6, 15] zip_longest() — на каждой итерации возвращает кортеж, содержащий элементы после- довательностей, которые расположены на одинаковом смещении. Если последователь- ности имеют разное количество элементов, вместо отсутствующего элемента вставляет- ся объект, указанный в параметре fillvalue . Формат функции: zip_longest(<Последовательность1>[, ..., <ПоследовательностьN>] [, fillvalue=None]) Глава 8. Списки, кортежи, множества и диапазоны 173 Примеры: >>> list(itertools.zip_longest([1, 2, 3], [4, 5, 6])) [(1, 4), (2, 5), (3, 6)] >>> list(itertools.zip_longest([1, 2, 3], [4])) [(1, 4), (2, None), (3, None)] >>> list(itertools.zip_longest([1, 2, 3], [4], fillvalue=0)) [(1, 4), (2, 0), (3, 0)] >>> list(zip([1, 2, 3], [4])) [(1, 4)] accumulate(<Последовательность>[, <функция>]) — на каждой итерации возвращает результат, полученный выполнением определенного действия над текущим элементом и результатом, полученным на предыдущей итерации. Выполняемая операция задается параметром <Функция> , а если он не указан, выполняется операция сложения. Функция, выполняющая операцию, должна принимать два параметра и возвращать результат. На первой итерации всегда возвращается первый элемент переданной последовательности: >>> # Выполняем сложение >>> list(itertools.accumulate([1, 2, 3, 4, 5, 6])) [1, 3, 6, 10, 15, 21] >>> # [1, 1+2, 3+3, 6+4, 10+5, 15+6] >>> # Выполняем умножение >>> def func(x, y): return x * y >>> list(itertools.accumulate([1, 2, 3, 4, 5, 6], func)) [1, 2, 6, 24, 120, 720] >>> # [1, 1*2, 2*3, 6*4, 24*5, 120*6] chain() — на каждой итерации возвращает элементы сначала из первой последователь- ности, затем из второй и т. д. Формат функции: chain(<Последовательность1>[, ..., <ПоследовательностьN>]) Примеры: >>> arr1, arr2, arr3 = [1, 2, 3], [4, 5], [6, 7, 8, 9] >>> list(itertools.chain(arr1, arr2, arr3)) [1, 2, 3, 4, 5, 6, 7, 8, 9] >>> list(itertools.chain("abc", "defg", "hij")) ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'] >>> list(itertools.chain("abc", ["defg", "hij"])) ['a', 'b', 'c', 'defg', 'hij'] chain.from_iterable(<Последовательность>) — аналогична функции chain() , но прини- мает одну последовательность, каждый элемент которой считается отдельной последо- вательностью: >>> list(itertools.chain.from_iterable(["abc", "defg", "hij"])) ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'] tee(<Последовательность>[, <Количество>]) — возвращает кортеж, содержащий не- сколько одинаковых итераторов для последовательности. Если второй параметр не ука- зан, то возвращается кортеж из двух итераторов: |