Программирование на Python 3. Руководство издательство СимволПлюс
Скачать 3.74 Mb.
|
128 Глава 2. Типы данных (format – формат вывода чисел. Если не задан, по умолчанию используется формат ".0f".) А ниже приводится пример командной строки, в которой установ лены оба аргумента: csv2html2_ans.py maxwidth=20 format=0.2f < mydata.csv > mydata.html Не забудьте изменить функцию print_line() так, чтобы она исполь зовала переменную format при выводе чисел – для этого вам придет ся передавать функции дополнительный аргумент, добавить одну строку и изменить еще одну строку. И это немного затронет функ цию main(). Функция process_options() должна содержать порядка двадцати пяти строк (включая девять строк с текстом сообщения о порядке использования). Это упражнение может оказаться слож ным для неопытных программистов. В состав примеров входят два файла с тестовыми данными: data/ co2sample.csv и data/co2fromfossilfuels.csv. Решение приводится в файле csv2html2_ans.py. В главе 5 мы увидим, как для обработки аргументов командной строки можно использовать модуль optparse. 3 Типы коллекций В предыдущей главе мы познакомились с наиболее важными фунда ментальными типами данных языка Python. В этой главе мы расши рим свои возможности, узнав, как объединять элементы данных вме сте, используя типы коллекций языка Python. В этой главе мы рас смотрим кортежи и списки, а также познакомимся с новыми типами коллекций, включая словари и множества, и детально изучим их. 1 В дополнение к коллекциям мы также узнаем, как создавать элементы данных, вмещающие в себя другие элементы данных (подобно струк турам в языках C и C++ и записям в языке Pascal). Такие элементы в случае необходимости могут интерпретироваться как единое целое, и при этом сохраняется возможность прямого доступа к отдельным эле ментам, хранящимся в них. Естественно, ничто не мешает вставлять такие агрегатные элементы в коллекции, как любые другие элементы. Наличие коллекций элементов данных существенно упрощает выпол нение операций, которые должны применяться к элементам, а также упрощает обработку коллекций элементов при чтении их из файлов. В этой главе мы рассмотрим основные приемы работы с файлами лишь в том объеме, который нам потребуется, отложив описание основных подробностей (включая обработку ошибок) до главы 7. После знакомства с отдельными типами коллекций мы посмотрим, как можно организовать обход коллекций в цикле, поскольку в языке Python для итераций через любые коллекции используются одни и те же синтаксические конструкции. Кроме этого, мы исследуем пробле мы и приемы копирования коллекций. 1 Определение того, что является последовательностью, множеством или отображением, в этой главе дается не с формальной, а с практической точ ки зрения. Более формальные определения даются в главе 8. • Последовательности • Множества • Отображения • Обход в цикле и копирование коллекций 130 Глава 3. Типы коллекций Последовательности Последовательности – это один из типов данных, поддерживающих оператор проверки на вхождение (in), функцию определения размера (len()), оператор извлечения срезов ([]) и возможность выполнения итераций. В языке Python имеется пять встроенных типов последова тельностей: bytearray, bytes, list, str и tuple – первые два будут описа ны отдельно, в главе 7. Ряд дополнительных типов последовательно стей реализован в стандартной библиотеке; наиболее примечательным из них является тип collections.namedtuple. При выполнении итераций все эти последовательности гарантируют строго определенный поря док следования элементов. Строки мы уже рассматривали в предыдущей главе, а в этом разделе познакомимся с кортежами, именован ными кортежами и списками. Кортежи Кортеж – это упорядоченная последовательность из ну ля или более ссылок на объекты. Кортежи поддержива ют тот же синтаксис получения срезов, что и строки. Это упрощает извлечение элементов из кортежа. Подобно строкам, кортежи относятся к категории неизменяемых объектов, поэтому мы не можем замещать или удалять какиелибо их элементы. Если нам необходимо иметь возможность изменять упорядоченную последователь ность, то вместо кортежей можно просто использовать списки или, если в программе уже используется кортеж, который нежелательно модифицировать, можно преоб разовать кортеж в список с помощью функции преобра зования list() и затем изменять полученный список. Тип данных tuple может вызываться как функция tup le() – без аргументов она возвращает пустой кортеж, с аргументом типа tuple возвращает поверхностную ко пию аргумента; в случае, если аргумент имеет другой тип, выполняется попытка преобразовать его в объект типа tuple. Эта функция принимает не более одного аргу мента. Кроме того, кортежи могут создаваться без ис пользования функции tuple(). Пустой кортеж создается с помощью пары пустых круглых скобок (), а кортеж, состоящий из одного или более элементов, может быть создан с помощью запятых. Иногда кортежи приходится заключать в круглые скобки, чтобы избежать синтакси ческой неоднозначности. Например, чтобы передать кортеж 1, 2, 3 в функцию, необходимо использовать та кую форму записи: function((1, 2, 3)). Строки, стр. 84 Извлечение срезов из строк, стр. 89 Поверхно стное и глубокое копирование, стр. 173 Последовательности 131 На рис. 3.1 показан кортеж t = "venus", – 28, "green", "21", 19.74 и ин дексы элементов внутри кортежа. Строки индексируются точно так же, но, если в строках каждой позиции соответствует единственный символ, то в кортежах каждой позиции соответствует единственная ссылка на объект. Кортежи предоставляют всего два метода: t.count(x), который возвра щает количество объектов x в кортеже t, и t.index(x), который возвра щает индекс самого первого (слева) вхождения объекта x в кортеж t или возбуждает исключение ValueError, если объект x отсутствует в корте же. (Эти методы имеются также и у списков.) Кроме того, кортежи могут использоваться с оператором + (конкатена ции), * (дублирования) и [] (получения среза), а операторы in и not in могут применяться для проверки на вхождение. Можно использовать также комбинированные операторы присваивания += и *=. Несмотря на то, что кортежи являются неизменяемыми объектами, при выпол нении этих операторов интерпретатор Python создает за кулисами но вый кортеж с результатом операции и присваивает ссылку на него объ екту, расположенному слева от оператора, то есть используется тот же самый прием, что и со строками. Кортежи могут сравниваться с помо щью стандартных операторов сравнения (<, <=, ==, !=, >=, >), при этом сравнивание производится поэлементно (и рекурсивно, при наличии вложенных элементов, таких как кортежи в кортежах). Рассмотрим несколько примеров получения срезов, начав с извлече ния единственного элемента и группы элементов: >>> hair = "black", "brown", "blonde", "red" >>> hair[2] 'blonde' >>> hair[3:] # то же, что и hair[1:] ('brown', 'blonde', 'red') Эта операция выполняется точно так же, как и в случае со строками, списками или любыми другими последовательностями. >>> hair[:2], "gray", hair[2:] (('black', 'brown'), 'gray', ('blonde', 'red')) Здесь мы попытались создать новый кортеж из 5 элементов, но в ре зультате получили кортеж с тремя элементами, содержащий два двух t[5] t[4] t[3] t[2] t[1] 'venus' 28 'green' '21' 19.74 t[0] t[1] t[2] t[3] t[4] Рис. 3.1. Позиции элементов в кортеже 132 Глава 3. Типы коллекций элементных кортежа. Это произошло потому, что мы применили опе ратор запятой к трем элементам (кортеж, строка и кортеж). Чтобы по лучить единый кортеж со всеми этими элементами, необходимо вы полнить конкатенацию кортежей: >>> hair[:2] + ("gray",) + hair[2:] ('black', 'brown', 'gray', 'blonde', 'red') Чтобы создать кортеж из одного элемента, необходимо поставить запя тую, но если запятую просто добавить, будет получено исключение TypeError (так как интерпретатор будет думать, что выполняется кон катенация строки и кортежа), поэтому необходимо использовать запя тую и круглые скобки. В этой книге (начиная с этого момента) мы будем использовать опреде ленный стиль записи кортежей. Когда кортеж будет стоять слева от двухместного оператора или справа от одноместного, мы будем опус кать круглые скобки. Во всех остальных случаях будут использовать ся круглые скобки. Ниже приводятся несколько примеров: a, b = (1, 2) # слева от двухместного оператора del a, b # справа от одноместного оператора def f(x): return x, x ** 2 # справа от одноместного оператора for x, y in ((1, 1), (2, 4), (3, 9)): # слева от двухместного оператора print(x, y) Совершенно необязательно следовать этому стилю записи – некоторые программисты предпочитают всегда использовать круглые скобки, что соответствует репрезентативной форме представления кортежей, одна ко другие используют скобки, только когда это строго необходимо. >>> eyes = ("brown", "hazel", "amber", "green", "blue", "gray") >>> colors = (hair, eyes) >>> colors[1][3:1] ('green', 'blue') В следующем примере мы вложили друг в друга два кортежа. Коллек ции допускают возможность вложения с любой глубиной вложенно сти. Оператор извлечения срезов [] может применяться для доступа к вложенным коллекциям столько раз, сколько это будет необходимо. Например: >>> things = (1, 7.5, ("pea", (5, "Xyz"), "queue")) >>> things[2][1][1][2] 'z' Рассмотрим этот пример по частям, начиная с выражения things[2], которое дает нам третий элемент кортежа (не забывайте, что первый элемент имеет индекс 0), который сам является кортежем ("pea", (5, "Xyz"), "queue") . Выражение things[2][1] дает нам второй элемент кор Последовательности 133 тежа things[2], который тоже является кортежем (5, "Xyz"). А выра жение things[2][1][1] дает нам второй элемент этого кортежа, который представляет строку "Xyz". Наконец, выражение things[2][1][1][2] да ет нам третий элемент (символ) строки, то есть символ "z". Кортежи могут хранить элементы любых типов, включая другие кол лекции, такие как кортежи и списки, так как на самом деле кортежи хранят ссылки на объекты. Использование сложных, вложенных структур данных, таких, как показано ниже, легко может создавать путаницу. Одно из решений этой проблемы состоит в том, чтобы да вать значениям индексов осмысленные имена. Например: >>> MANUFACTURER, MODEL, SEATING = (0, 1, 2) >>> MINIMUM, MAXIMUM = (0, 1) >>> aircraft = ("Airbus", "A320200", (100, 220)) >>> aircraft[SEATING][MAXIMUM] 220 Конечно, в таком виде программный код выглядит более осмыслен ным, чем простое выражение aircraft[2][1], но при этом приходится создавать большое число переменных, да и выглядит он несколько уродливо. В следующем подразделе мы познакомимся с более привле кательной альтернативой. В первых двух строках вышеприведенного фрагмента мы выполнили присваивание кортежам. Когда справа от оператора присваивания указывается последовательность (в данном случае – это кортежи), а слева указан кортеж, мы говорим, что последовательность справа распаковывается ). Операция распаковывания последовательностей может использоваться для организации обмена значений между пере менными, например: a, b = (b, a) Строго говоря, круглые скобки справа не являются обязательными, но, как уже отмечалось выше, в этой книге мы используем стиль запи си, когда скобки опускаются только в левом операнде двухместного оператора и в правом операнде одноместного оператора и используют ся во всех остальных случаях. Мы уже сталкивались с примерами распаковывания последовательно стей в контексте оператора цикла for ... in. Следующий пример при водится только в качестве напоминания: for x, y in ((3, 4), (5, 12), (28, 45)): print(math.hypot(x, y)) Здесь выполняется обход кортежа, состоящего из двухэлементных кортежей, каждый из которых распаковывается в переменные x и y. 134 Глава 3. Типы коллекций Именованные кортежи Именованные кортежи ведут себя точно так же, как и обычные корте жи, и не уступают им в производительности. Отличаются они возмож ностью ссылаться на элементы кортежа не только по числовому индек су, но и по имени, что в свою очередь позволяет создавать сложные аг регаты из элементов данных. В модуле collections имеется функция namedtuple(). Эта функция ис пользуется для создания собственных типов кортежей. Например: Sale = collections.namedtuple("Sale", "productid customerid date quantity price") Первый аргумент функции collections.namedtuple() – это имя создавае мого кортежа. Второй аргумент – это строка имен, разделенных пробе лами, для каждого элемента, который будет присутствовать в этом кортеже. Первый аргумент и имена во втором аргументе должны быть допустимыми идентификаторами языка Python. Функция возвращает класс (тип данных), который может использоваться для создания име нованных кортежей. Так, в примере выше мы можем интерпретиро вать имя Sale как имя любого другого класса (такого как tuple) в языке Python и создавать объекты типа Sale. 1 Например: sales = [] sales.append(Sale(432, 921, "20080914", 3, 7.99)) sales.append(Sale(419, 874, "20080915", 1, 18.49)) В этом примере мы создали список из двух элементов типа Sale, то есть из двух именованных кортежей. Мы можем обращаться к элементам таких кортежей по их индексам – например, обратиться к элементу price в первом элементе списка sales можно с помощью выражения sales[0][ – 1] (вернет значение 7.99) – или по именам, которые делают программный код более удобочитаемым: total = 0 for sale in sales: total += sale.quantity * sale.price print("Total ${0:.2f}".format(total)) # выведет: Total $42.46 Очень часто простоту и удобство, которые предоставляют именован ные кортежи, можно обратить на пользу делу. Например, ниже приво дится версия примера «aircraft» из предыдущего подраздела (стр. 133), имеющая более аккуратный вид: >>> Aircraft = collections.namedtuple("Aircraft", ... "manufacturer model seating") >>> Seating = collections.namedtuple("Seating", "minimum maximum") 1 Примечание для программистов, использующих объектноориентирован ный стиль: каждый класс, созданный таким способом, будет являться под классом класса tuple. Последовательности 135 >>> aircraft = Aircraft("Airbus", "A320200", Seating(100, 220)) >>> aircraft.seating.maximum 220 Уже видно, что именованные кортежи могут быть очень удобны; кро ме того, в главе 6 мы перейдем к изучению объектноориентированно го программирования, где выйдем за пределы простых именованных кортежей и узнаем, как создавать свои собственные типы данных, ко торые могут не только хранить элементы данных, но и иметь собствен ные методы. Списки Список – это упорядоченная последовательность из нуля или более ссылок на объекты. Списки поддерживают тот же синтаксис получения срезов, что и строки с кортежа ми. Это упрощает извлечение элементов из списка. В от личие от строк и кортежей списки относятся к катего рии изменяемых объектов, поэтому мы можем замещать или удалять любые их элементы. Кроме того, существу ет возможность вставлять, замещать и удалять целые срезы списков. Тип данных list может вызываться как функция list() – без аргументов она возвращает пустой список, с аргу ментом типа list возвращает поверхностную копию ар гумента; в случае, если аргумент имеет другой тип, вы полняется попытка преобразовать его в объект типа list. Эта функция принимает не более одного аргумента. Кро ме того, списки могут создаваться без использования функции list(). Пустой список создается с помощью па ры пустых квадратных скобок [], а список, состоящий из одного или более элементов, может быть создан с по мощью последовательности элементов, разделенных за пятыми, заключенной в квадратные скобки. Другой спо соб создания списков заключается в использовании гене раторов списков – эта тема будет рассматриваться ниже в этом подразделе. Поскольку все элементы списка в действительности являются ссылка ми на объекты, списки, как и кортежи, могут хранить элементы лю бых типов данных, включая коллекции, такие как списки и кортежи. Списки могут сравниваться с помощью стандартных операторов срав нения (<, <=, ==, !=, >=, >), при этом сравнивание производится поэле ментно (и рекурсивно, при наличии вложенных элементов, таких как списки или кортежи в списках). В результате выполнения операции присваивания L = [ – 17.5, "kilo", 49, "V", ["ram", 5, "echo"], 7] мы получим список, как показано на рис. 3.2. Извлечение срезов из строк, стр. 89 Поверхно стное и глубокое копирование, стр. 173 Генераторы списков, стр. 142 136 Глава 3. Типы коллекций К спискам, таким как L, мы можем применять оператор извлечения среза, повторяя его столько раз, сколько потребуется для доступа к эле ментам в списке, как показано ниже: L[0] == L[6] == 17.5 L[1] == L[5] == 'kilo' L[1][0] == L[5][0] == 'k' L[4][2] == L[4][1] == L[2][2] == L[2][1] == 'echo' L[4][2][1] == L[4][2][3] == L[2][1][1] == L[2][1][3] == 'c' Списки, как и кортежи, могут вкладываться друг в друга; допускают выполнение итераций по их элементам и извлечение срезов. Все при меры с кортежами, которые приводились в предыдущем подразделе, будут работать точно так же, если вместо кортежей в них будут ис пользованы списки. Списки поддерживают операторы проверки на вхождение in и not in, оператор конкатенации +, оператор расширения += (то есть добавляет операнд справа в конец списка) и операторы дуб лирования * и *=. Списки могут также использоваться в качестве аргу ментов функции len() и в инструкции del, которая будет рассматри ваться в этом подразделе и которая описывается во врезке «Удаление элементов с помощью инструкции del» на стр. 139. Кроме того, списки предоставляют методы, перечисленные в табл. 3.1. Таблица 3.1. Методы списков |