Марк Лутц - Изучаем Python, Четвертое издание (1184811), страница 71
Текст из файла (страница 71)
Для работы в Интернете поддержка CGI-сценариев, предусмотренная в языкеPython, также обеспечивает интерфейс, напоминающий словарь. Вызовметода cgi.FieldStorage возвращает объект, по своим характеристикамнапоминающий словарь, – с одной записью для каждого поля ввода, находящегося на клиентской веб-странице:import cgiform = cgi.FieldStorage()# Анализирует данные формыif ‘name’ in form:showReply(‘Hello, ‘ + form[‘name’].value)Все эти объекты (и словари в том числе) являются примерами отображений. Как только вы овладеете словарными интерфейсами, вы обнаружите, что они имеют отношение ко множеству встроенных инструментов языка Python.275Словари в действииДругие способы создания словарейНаконец, обратите внимание, что благодаря практической ценности словарейс течением времени способы их создания пополнялись. В Python 2.3 и болеепоздних версиях, например, последние два вызова конструктора dict (в действительности – имени типа) в следующем ниже примере имеют тот же эффект,что и литералы и форма присваивания по отдельным ключам в примере выше:{‘name’: ‘mel’, ‘age’: 45}# Традиционное литеральное выражениеD = {}D[‘name’] = ‘mel’D[‘age’] = 45# Динамическое присваивание по ключамdict(name=’mel’, age=45)# Форма именованных аргументовdict([(‘name’, ‘mel’), (‘age’, 45)]) # Кортежи ключ/значениеВсе четыре варианта создают один и тот же словарь, содержащий два элемента,которые удобно использовать в следующих случаях:•• Первый вариант удобен, если содержимое всего словаря известно заранее.•• Второй вариант удобно использовать, когда необходимо динамически создавать словарь по одному полю за раз.•• Третий вариант с использованием именованных аргументов даже компактнее, чем литералы, но он требует, чтобы все ключи были строками.•• Последний вариант удобен, когда ключи и значения во время выполненияпрограммы необходимо хранить в виде последовательностей.С именованными аргументами мы уже встречались ранее, когда рассматривалиоперацию сортировки.
Третья форма, показанная в листинге, стала особеннопопулярной в последнее время, как наиболее компактная (и, как следствие, менее подверженная ошибкам). Как было показано в конце табл. 8.2, последнийвариант также часто используется в соединении с функцией zip, которая позволяет объединить отдельные списки ключей и значений, создаваемые динамически во время выполнения (например, при извлечении данных из столбцовв файле). Подробнее об этой возможности рассказывается в следующем разделе.Если значения всех ключей словаря остаются все время одними и теми же,можно использовать специализированную форму инициализации словаря,при использовании которой достаточно просто передать список ключей и начальное значение (по умолчанию используется значение None):>>> dict.fromkeys([‘a’, ‘b’], 0){‘a’: 0, ‘b’: 0}В настоящее время вполне можно обойтись простыми литералами и операциейприсваивания по ключам, но вы наверняка найдете применение всем упомянутым вариантам создания словарей, как только приступите к созданию настоящих, гибких и динамических программ на языке Python.В листингах, которые приводятся в этом разделе, представлены различныеспособы создания словарей, общие для обеих версий Python 2.6 и 3.0.
Однакосуществует еще один способ создания словарей, доступный только в версии Py���thon 3.0 (и выше): генератор словарей. Чтобы посмотреть, как используется этаформа, мы должны перейти к следующему разделу.276Глава 8. Списки и словариИзменения в словарях в Python 3.0До сих пор в этой главе мы рассматривали основные операции над словарями,общие для обеих версий Python, однако в Python 3.0 функциональные возможности словарей несколько изменились. Если вы пользуетесь интерпретаторомверсии 2.X, вы можете столкнуться с некоторыми особенностями словарей,которые либо изменились, либо вообще отсутствуют в версии 3.0. Кроме того,версия 3.0 расширяет словари дополнительными особенностями, недоступными в Python 2.X. В частности, в версии 3.0 словари:•• Поддерживают новые выражения генераторов словарей, близко напоминающие генераторы списков и множеств.•• Методы D.keys, D.values и D.items возвращают итерируемые представлениявместо списков.•• Вследствие особенности, описанной в предыдущем пункте, требуют по иному выполнять обход ключей в отсортированном порядке.•• Больше не поддерживают возможность непосредственного сравниваниямежду собой – теперь сравнивание должно выполняться вручную.•• Больше не поддерживают метод D.has_key – вместо него следует использовать оператор in проверки на вхождение.Давайте посмотрим, что нового появилось в словарях с выходом версии 3.0.Генераторы словарейКак отмечалось в конце предыдущего раздела, в версии 3.0 появилась возможность создавать словари с помощью генераторов словарей.
Как и генераторымножеств, с которыми мы встречались в главе 5, генераторы словарей доступны только в версии 3.0 (они недоступны в версии 2.6). Подобно давно существующим генераторам списков, с которыми мы встречались в главе 4 и ранеев этой главе, генераторы словарей выполняют цикл, отбирают пары ключ/значение в каждой итерации и заполняют ими новый словарь. Значения, получаемые в ходе итераций, доступны в виде переменной цикла.Например, одним из стандартных способов динамической инициализациисловаря в версиях 2.6 и 3.0 является использование функции zip для объединения ключей и значений с последующей передачей результата функции dict.Как мы узнаем в главе 13, функция zip позволяет единственным вызовом создать словарь из списков ключей и значений. Если множество ключей и значений заранее не известно, вы всегда можете поместить их в списки в процессевычислений и затем объединить их воедино:>>> list(zip([‘a’, ‘b’, ‘c’], [1, 2, 3])) # Объединить ключи и значения[(‘a’, 1), (‘b’, 2), (‘c’, 3)]>>> D = dict(zip([‘a’, ‘b’, ‘c’], [1, 2, 3]))>>> D{‘a’: 1, ‘c’: 3, ‘b’: 2}# Создать словарь из результата# вызова функции zipВ Python 3.0 того же эффекта можно добиться с помощью генератора словарей.В следующем примере демонстрируется создание нового словаря из пар ключ/значение, которые извлекаются из результата вызова функции zip (это выражение читается практически точно так же, хотя и выглядит немного сложнее):Словари в действии277C:\misc> c:\python30\python# Использование генератора словарей>>> D = {k: v for (k, v) in zip([‘a’, ‘b’, ‘c’], [1, 2, 3])}>>> D{‘a’: 1, ‘c’: 3, ‘b’: 2}Генераторы словарей выглядят менее компактными, но они гораздо более универсальны, чем можно было бы предположить, исходя из этого примера, – мыможем использовать их для отображения единственной последовательностизначений в словари, вычисляя ключи с помощью выражений, как обычныезначения:>>> D = {x: x ** 2 for x in [1, 2, 3, 4]} # Или: range(1, 5)>>> D{1: 1, 2: 4, 3: 9, 4: 16}>>> D = {c: c * 4 for c in ‘SPAM’}# Цикл через итерируемый объект>>> D{‘A’: ‘AAAA’, ‘P’: ‘PPPP’, ‘S’: ‘SSSS’, ‘M’: ‘MMMM’}>>> D = {c.lower(): c + ‘!’ for c in [‘SPAM’, ‘EGGS’, ‘HAM’]}>>> D{‘eggs’: ‘EGGS!’, ‘ham’: ‘HAM!’, ‘spam’: ‘SPAM!’}Генераторы словарей удобно использовать для инициализации словарей изсписков ключей, почти так же, как это делает метод fromkeys, с которым мывстречались в предыдущем разделе:>>> D = dict.fromkeys([‘a’, ‘b’, ‘c’], 0) # Инициализация списком ключей>>> D{‘a’: 0, ‘c’: 0, ‘b’: 0}>>> D = {k:0 for k in [‘a’, ‘b’, ‘c’]} # То же самое, но с помощью>>> D# генератора словаря{‘a’: 0, ‘c’: 0, ‘b’: 0}>>> D = dict.fromkeys(‘spam’)# Из другого итерируемого объекта,>>> D# используются значения по умолчанию{‘a’: None, ‘p’: None, ‘s’: None, ‘m’: None}>>> D = {k: None for k in ‘spam’}>>> D{‘a’: None, ‘p’: None, ‘s’: None, ‘m’: None}Подобно родственным инструментам, генераторы словарей поддерживают дополнительные возможности, которые не были продемонстрированы здесь,включая вложенные циклы и условные инструкции if.
К сожалению, чтобыполностью понять все возможности генераторов словарей, нам необходимопознакомиться поближе с концепцией и инструкциями итераций в языкеPython, – но у нас пока недостаточно знаний, чтобы обсуждать эту тему. Мыпознакомимся поближе со всеми разновидностями генераторов (списков, множеств и словарей) в главах 14 и 20, поэтому отложим пока обсуждение деталей.Кроме того, в главе 13 мы поближе познакомимся со встроенной функцией zip,которую использовали в этом разделе, когда будем исследовать циклы for.Представления словарейВ версии 3.0 методы словарей keys, values и items возвращают объекты представлений, тогда как в версии 2.6 они возвращают списки.
Объекты представ-278Глава 8. Списки и словарилений – это итерируемые объекты, то есть объекты, которые вместо всего списка значений возвращают по одному значению за одно обращение. Кроме того,что они являются итерируемыми объектами, представления словарей такжесохраняют оригинальный порядок следования компонентов словаря, отражают результаты операций, которые выполняются над словарем, и поддерживают операции над множествами. С другой стороны, они не являются спискамии не поддерживают такие операции, как обращение к элементам по индексамили метод sort списков, а также не отображают значения своих элементов привыводе.Понятие итерируемого объекта более формально будет рассматриваться в главе 14, а пока нам достаточно будет знать, что результаты этих трех методовнеобходимо обертывать вызовом встроенной функции list, – если появитсянеобходимость применить операции над списками или отобразить значенияэлементов:>>> D = dict(a=1, b=2, c=3)>>> D{‘a’: 1, ‘c’: 3, ‘b’: 2}>>> K = D.keys() # В версии 3.0 создаст объект представления, а не список>>> K<dict_keys object at 0x026D83C0>>>> list(K)[‘a’, ‘c’, ‘b’]# Принудительное создание списка в версии 3.0>>> V = D.values() # То же относится к представлениям значений и элементов>>> V<dict_values object at 0x026D8260>>>> list(V)[1, 3, 2]>>> list(D.items())[(‘a’, 1), (‘c’, 3), (‘b’, 2)]>>> K[0]# Ошибка при попытке выполнить операцию над спискомTypeError: ‘dict_keys’ object does not support indexing>>> list(K)[0]‘a’Кроме случаев отображения результатов в интерактивной оболочке, вы едва либудете обращать внимание на эту особенность, потому что конструкции обхода элементов в цикле в языке Python автоматически заставляют итерируемыеобъекты возвращать по одному результату в каждой итерации:>>> for k in D.keys(): print(k) # Работа с итераторами в циклах...# выполняется автоматическиacbКроме того, в версии 3.0 словари по-прежнему остаются итерируемыми объектами, которые последовательно возвращают ключи, как и в версии 2.6, – чтоисключает необходимость вызывать метод keys:>>> for key in D: print(key) # В итерациях по-прежнему не обязательно...# вызывать метод keys()279Словари в действииacbВ отличие от списков, возвращаемых в виде результатов в версии 2.��������X�������, представления словарей в версии 3.0 способны динамически отражать все последующие изменения в словарях, выполненные уже после создания объекта отображения:>>> D = {‘a’:1, ‘b’:2, ‘c’:3}>>> D{‘a’: 1, ‘c’: 3, ‘b’: 2}>>> K = D.keys()>>> V = D.values()>>> list(K)# Представления сохраняют оригинальный[‘a’, ‘c’, ‘b’]# порядок следования ключей в словаре>>> list(V)[1, 3, 2]>>> del D[‘b’]>>> D{‘a’: 1, ‘c’: 3}# Изменяет словарь непосредственно>>> list(K)[‘a’, ‘c’]>>> list(V)[1, 3]# Изменения в словаре отражаются на объектах представлений# Но это не так в версии 2.X!Представления словарей и множестваКроме того, в отличие от списков результатов, возвращаемых в версии 2.X,объекты представлений в версии 3.0, возвращаемые методом keys, похожи намножества и поддерживают операции над множествами, такие как пересечение и объединение.