Марк Лутц - Изучаем Python, Четвертое издание (1184811), страница 37
Текст из файла (страница 37)
Чтобыувеличить список, необходимо воспользоваться таким методом, как append.Вложенные спискиОдна из замечательных особенностей базовых типов языка Python состоитв том, что они поддерживают возможность создания вложенных конструкцийпроизвольной глубины и в любых комбинациях (например, можно создатьсписок, содержащий словарь, который содержит другой список, и так далее).Одно из очевидных применений этой особенности – представление матриц, или«многомерных массивов» в языке Python. Делается это с помощью списка, содержащего вложенные списки:>>> M = [[1, 2, 3], # Матрица 3 x 3 в виде вложенных списков[4, 5, 6], # Выражение в квадратных скобках может[7, 8, 9]] # занимать несколько строк>>> M[[1, 2, 3], [4, 5, 6], [7, 8, 9]]Здесь мы реализовали список, состоящий из трех других списков.
В результате была получена матрица чисел 3 x 3. Обращаться к такой структуре можноразными способами:>>> M[1][4, 5, 6]# Получить строку 2>>> M[1][2] # Получить строку 2, а затем элемент 3 в этой строке6Первая операция в этом примере возвращает вторую строку целиком, а вторая – третий элемент в этой строке. Соединение операций индексированияпозволяет все дальше и дальше погружаться вглубь вложенной структурыобъектов.1Генераторы списковПомимо обычных операций над последовательностями и методов списковPython предоставляет возможность выполнять более сложные операции над1Такая организация матриц вполне пригодна для решения небольших задач, но дляреализации более сложных программ числовой обработки информации желательноиспользовать специализированные расширения, например NumPy. Такого рода инструменты позволяют хранить и обрабатывать матрицы намного эффективнее, чемтакая структура, реализованная в виде вложенных списков.
Как уже говорилось,расширение NumPy превращает Python в свободный и более мощный эквивалентсистемы MatLab, и такие организации, как NASA, Los Alamos и JPMorgan Chase,используют его для решения научных и финансовых задач. Дополнительную информацию об этом расширении вы без труда найдете в Сети.136Глава 4. Введение в типы объектов языка Pythonсписками, известные как выражения генераторов списков (list comprehensionexpression), которые представляют эффективный способ обработки такихструктур, как приведенная в примере матрица.
Предположим, например, чтонам требуется извлечь из нашей матрицы второй столбец. Строку легко можнополучить, выполнив операцию индексирования, потому что матрица хранится в виде строк, однако, благодаря генераторам списков, получить столбец ничуть не сложнее:>>> col2 = [row[1] for row in M] # Выбирает элементы второго столбца>>> col2[2, 5, 8]>>> M# Матрица не изменилась[[1, 2, 3], [4, 5, 6], [7, 8, 9]]Генераторы списков следуют традиции системы представления множеств; онипозволяют создавать новые списки, выполняя выражение для каждого элемента в последовательности, по одному за раз, слева направо. Генераторы списковзаключены в квадратные скобки (чтобы отразить тот факт, что они создаютсписок) и составлены из выражения и конструкции цикла, которые используют одно и то же имя переменной (в данном случае row).
В предыдущем примерегенератор списков интерпретируется так: «Получить элементы row[1] из каждой строки матрицы M и создать из них новый список». Результатом являетсяновый список, содержащий значения из второго столбца матрицы.На практике генераторы списков могут приобретать еще более сложную форму:>>> [row[1] + 1 for row in M] # Добавить 1 к каждому элементу в столбце 2[3, 6, 9]>>> [row[1] for row in M if row[1] % 2 == 0] # отфильтровать нечетные значения[2, 8]Первая операция в этом примере прибавляет 1 к значениям всех отобранныхэлементов, а вторая использует условный оператор if для исключения из результата нечетных чисел с помощью операции деления по модулю – % (остатокот деления).
Генераторы списков, применяемые к спискам, возвращают в качестве результатов новые списки, но могут использоваться и для любых другихобъектов, допускающих выполнение итераций. Например, ниже показано использование генератора списков для обхода жестко заданного в программномкоде списка координат и строк:>>> diag = [M[i][i] for i in [0, 1, 2]] # Выборка элементов диагонали матрицы>>> diag[1, 5, 9]>>> doubles = [c * 2 for c in ‘spam’] # Дублирование символов в строке>>> doubles[‘ss’, ‘pp’, ‘aa’, ‘mm’]Генераторы списков и родственные им встроенные функции map и filter – намой взгляд, достаточно сложная тема, чтобы говорить о них здесь более подробно.
Главная цель этого краткого обзора состоит в том, чтобы проиллюстрировать наличие как простых, так и очень сложных инструментов в арсеналеPython. Можно обойтись и без генераторов списков, но на практике они оказываются очень удобными и нередко обеспечивают более высокую производи-137Словарительность при работе со списками. Кроме того, их можно применять к любымтипам, являющимся последовательностями в языке Python, а также к некоторым типам, которые не являются последовательностями. Мы еще будем говорить об этих выражениях далее в этой книге.Забегая вперед, отмечу, что в последних версиях Python����������������������������������������������можно также использовать конструкции генераторов списков, заключенные в круглые скобки, длясоздания генераторов, которые воспроизводят результаты по требованию. Например, ниже показано, как с помощью встроенной функции sum можно суммировать элементы в последовательности:>>> G = (sum(row) for row in M) # Генератор, возвращающий суммы элементов строк>>> next(G)6>>> next(G)# Вызов в соответствии с протоколом итераций15Того же эффекта можно добиться с помощью встроенной функции map, генерируя результаты за счет передачи элементов другой функции.
Обертываниевызова этой функции в список (в версии Python 3.0) вынуждает ее вернуть всезначения:>>> list(map(sum, M)) # Отобразить sum на элементы в M[6, 15, 24]В Python 3.0 синтаксис генераторов списков может также использоваться длясоздания множеств и словарей:>>> {sum(row) for row in M}# Создаст множество сумм строк{24, 6, 15}>>> {i : sum(M[i]) for i in range(3)} # Таблица пар ключ/значение сумм строк{0: 6, 1: 15, 2: 24}Фактически в версии 3.0 с помощью подобных выражений-генераторов можносоздавать списки, множества и словари:>>> [ord(x) for x in ‘spaam’]#[115, 112, 97, 97, 109]>>> {ord(x) for x in ‘spaam’}#{112, 97, 115, 109}>>> {x: ord(x) for x in ‘spaam’} #{‘a’: 97, ‘p’: 112, ‘s’: 115, ‘m’:Список кодов символовМножества ликвидируют дубликатыКлючи словарей являются уникальными109}Однако нам следует двигаться дальше, чтобы познакомиться с такими объектами, как генераторы, множества и словари.СловариСловари в языке Python – это нечто совсем иное (по выражению Монти Пайтона); они вообще не являются последовательностями, это то, что известно какотображения.
Отображения – это коллекции объектов, но доступ к ним осуществляется не по определенным смещениям от начала коллекции, а по ключам. В действительности отображения вообще не подразумевают какого-либоупорядочения элементов по их позиции, они просто отображают ключи на связанные с ними значения. Словари – единственный тип отображения в наборебазовых объектов Python – также относятся к классу изменяемых объектов:138Глава 4. Введение в типы объектов языка Pythonони могут изменяться непосредственно и в случае необходимости могут увеличиваться и уменьшаться в размерах подобно спискам.Операции над отображениямиКогда словарь определяется как литерал, программный код определения заключается в фигурные скобки и состоит из последовательности пар «ключ:значение».
Словари удобно использовать всегда, когда возникает необходимость связать значения с ключами, например чтобы описать свойства чеголибо. В качестве примера рассмотрим следующий словарь, состоящий изтрех элементов (с ключами «food» (продукт питания), «quantity» (количество)и «color» (цвет)):>>> D = {‘food’: ‘Spam’, ‘quantity’: 4, ‘color’: ‘pink’}Мы можем обращаться к элементам этого словаря по ключам и изменять значения, связанные с ключами. Для доступа к элементам словаря используетсятот же синтаксис, который используется для обращения к элементам последовательностей, только в квадратных скобках указывается не смещение относительно начала последовательности, а ключ:>>> D[‘food’]‘Spam’# Получить значение, связанное с ключом ‘food’>>> D[‘quantity’] += 1 # Прибавить 1 к значению ключа ‘quantity’>>> D{‘food’: ‘Spam’, ‘color’: ‘pink’, ‘quantity’: 5}Несмотря на то, что форма определения словаря в виде литерала, заключенного в фигурные скобки, достаточно наглядна, на практике чаще встречаютсядругие способы создания словарей.
Следующий пример начинается с созданияпустого словаря, который затем заполняется по одному ключу за раз. В отличие от списков, не допускающих присваивание значений отсутствующим элементам, присваивание значения по несуществующему ключу в словаре приводит к созданию этого ключа:>>>>>>>>>>>>D = {}D[‘name’] = ‘Bob’ # В результате присваивания создается ключD[‘job’] = ‘dev’D[‘age’] = 40>>> D{‘age’: 40, ‘job’: ‘dev’, ‘name’: ‘Bob’}>>> print(D[‘name’])BobВ этом примере ключи словаря играют роль имен полей в записи, которая описывает некоторого человека. В других приложениях словари могут использоваться для замены операций поиска, поскольку обращение к элементу словаряпо ключу обычно выполняется быстрее, чем поиск, реализованный на языкеPython.Еще раз о вложенностиВ предыдущем примере словарь использовался для описания гипотетическойперсоны с помощью трех ключей.
Теперь предположим, что информация име-139Словариет более сложную структуру. Возможно, придется записать имя и фамилию,а также несколько названий должностей, занимаемых одновременно. Этоприводит к необходимости использования вложенных объектов Python. Словарь в следующем примере определен в виде литерала и имеет более сложнуюструктуру:>>> rec = {‘name’: {‘first’: ‘Bob’, ‘last’: ‘Smith’},‘job’: [‘dev’, ‘mgr’],‘age’: 40.5}Здесь мы опять имеем словарь, содержащий три ключа верхнего уровня (ключи «name» (имя), «job» (должность) и «age» (возраст)), однако значения имеютболее сложную структуру: для описания имени человека используется вложенный словарь, чтобы обеспечить поддержку имен, состоящих из несколькихчастей, и для перечисления занимаемых должностей используется вложенныйсписок, что обеспечит возможность расширения в будущем.
К компонентамэтой структуры можно обращаться почти так же, как мы делали это в случаес матрицей, но на этот раз вместо числовых индексов мы используем ключисловаря:>>> rec[‘name’]# ‘Name’ – это вложенный словарь{‘last’: ‘Smith’, ‘first’: ‘Bob’}>>> rec[‘name’][‘last’] # Обращение к элементу вложенного словаря‘Smith’>>> rec[‘job’][‘dev’, ‘mgr’]# ‘Job’ – это вложенный список>>> rec[‘job’][-1]‘mgr’# Обращение к элементу вложенного списка>>> rec[‘job’].append(‘janitor’) # Расширение списка должностей Боба (Bob)>>> rec{‘age’: 40.5, ‘job’: [‘dev’, ‘mgr’, ‘janitor’], ‘name’: {‘last’: ‘Smith’,‘first’: ‘Bob’}}Обратите внимание, как последняя операция в этом примере выполняет расширение вложенного списка. Так как список должностей – это отдельный отсловаря участок в памяти, он может увеличиваться и уменьшаться без какихлибо ограничений (размещение объектов в памяти будет обсуждаться в этойкниге позже).Основная цель демонстрации этого примера состоит в том, чтобы показать вамгибкость базовых типов данных в языке Python.