Если в словаре слово записано неправильно, как нам об этом узнать?
Стивен Райт
Словарь очень похож на список, но порядок элементов в нем не имеет значения и они не выбираются смещением, таким как 0 и 1. Вместо этого для каждого значения вы указываете связанный с ним уникальный ключ. Этот ключ чаще всего представляет собой строку, но, в принципе, может быть любым из неизменяемых типов: булевой переменной, целым числом, числом с плавающей точкой, кортежем, строкой, а также другими объектами, с которыми вы познакомитесь далее. Словари являются изменяемыми, а это значит, что вы можете добавить, удалить и изменить их элементы, имеющие вид «ключ — значение».
Если вы работали с другими языками программирования, которые поддерживают только массивы и списки, то полюбите словари.
В других языках словари могут называться ассоциативнымимассивами, хешами или хеш-таблицей. В Python словарь также принято называть dict.
Чтобы создать словарь, нужно заключить в фигурные скобки ({}) разделенные запятыми пары ключ:значение. Самым простым словарем является пустой словарь, не содержащий ни ключей, ни значений:
>>> empty_dict = {}
>>> empty_dict
{}
Создадим небольшой словарь, включающий цитаты из «Словаря сатаны» Амброза Бирса:
>>> bierce = {
... "day": "A period of twenty-four hours, mostly misspent",
... "positive": "Mistaken at the top of one's voice",
... "misfortune": "The kind of fortune that never misses",
... }
>>>
Ввод имени словаря в интерактивный интерпретатор выведет все его ключи и значения:
>>> bierce
{'day': 'A period of twenty-four hours, mostly misspent',
'positive': "Mistaken at the top of one's voice",
'misfortune': 'The kind of fortune that never misses'}
В Python допускается наличие запятой после последнего элемента списка, кортежа или словаря. Кроме того, не обязательно делать отступы, как делал я в предыдущем примере; когда вы вводите ключи и значения внутри фигурных скобок — это просто улучшает читабельность.
Некоторым людям не нравится печатать так много фигурных скобок и кавычек. И вы можете создать словарь, передав именованные аргументы и значения в функцию dict().
Традиционный способ создания словаря:
>>> acme_customer = {'first': 'Wile', 'middle': 'E', 'last': 'Coyote'}
>>> acme_customer
{'first': 'Wile', 'middle': 'E', 'last': 'Coyote'}
С использованием dict():
>>> acme_customer = dict(first="Wile", middle="E", last="Coyote")
>>> acme_customer
{'first': 'Wile', 'middle': 'E', 'last': 'Coyote'}
Одним из ограничений второго способа является то, что имена аргументов должны представлять собой корректные имена переменных (в них не должны использоваться пробелы и ключевые слова):
>>> x = dict(name="Elmer", def="hunter")
File "<stdin>", line 1
x = dict(name="Elmer", def="hunter")
^
SyntaxError: invalid syntax
С помощью функции dict() можно преобразовывать двузначные последовательности в словари. (Иногда вы можете столкнуться с такими последовательностями «ключ — значение», как «Стронций, 90, углерод, 14».) Первый элемент каждой последовательности применяется как ключ, второй — как значение.
Для начала рассмотрим небольшой пример использования lol — списка, состоящего из двузначных списков:
>>> lol = [ ['a', 'b'], ['c', 'd'], ['e', 'f'] ]
>>> dict(lol)
{'c': 'd', 'a': 'b', 'e': 'f'}
Мы могли бы использовать любую последовательность, содержащую последовательности из двух элементов. Рассмотрим другие примеры.
Список, содержащий двухэлементные кортежи:
>>> lot = [ ('a', 'b'), ('c', 'd'), ('e', 'f') ]
>>> dict(lot)
{'c': 'd', 'a': 'b', 'e': 'f'}
Кортеж, включающий двухэлементные списки:
>>> tol = (['a', 'b'], ['c', 'd'], ['e', 'f'])
>>> dict(tol)
{'c': 'd', 'a': 'b', 'e': 'f'}
Список, содержащий двухсимвольные строки:
>>> los = [ 'ab', 'cd', 'ef' ]
>>> dict(los)
{'c': 'd', 'a': 'b', 'e': 'f'}
Кортеж, содержащий двухсимвольные строки:
>>> tos = ('ab', 'cd', 'ef')
>>> dict(tos)
{'c': 'd', 'a': 'b', 'e': 'f'}
В подразделе «Итерируем по нескольким последовательностям с помощью функции zip()» главы 7 вы уже познакомились с функцией zip(), которая позволит вам легко создавать такие двухэлементные последовательности.
Добавить элемент в словарь довольно легко. Просто обратитесь к элементу по его ключу и присвойте ему значение. Если ключ уже существует в словаре, имеющееся значение будет заменено новым. Если ключ новый, он и указанное значение будут добавлены в словарь. В отличие от списков, здесь вам не нужно беспокоиться о том, что Python сгенерирует исключение во время присваивания нового элемента, если вы укажете индекс, который выходит за пределы существующего диапазона.
Сейчас мы создадим словарь с перечислением участников комик-группы из Великобритании Monty Python, используя их фамилии в качестве ключей, а имена — в качестве значений:
>>> pythons = {
... 'Chapman': 'Graham',
... 'Cleese': 'John',
... 'Idle': 'Eric',
... 'Jones': 'Terry',
... 'Palin': 'Michael',
... }
>>> pythons
{'Chapman': 'Graham', 'Cleese': 'John', 'Idle': 'Eric',
'Jones': 'Terry', 'Palin': 'Michael'}
Не хватает одного участника — уроженца Америки Терри Гиллиама. Здесь вы увидите попытку неизвестного программиста его добавить. Однако программист ошибся, когда вводил имя:
>>> pythons['Gilliam'] = 'Gerry'
>>> pythons
{'Chapman': 'Graham', 'Cleese': 'John', 'Idle': 'Eric',
'Jones': 'Terry', 'Palin': 'Michael', 'Gilliam': 'Gerry'}
А вот код другого программиста, который исправил эту ошибку:
>>> pythons['Gilliam'] = 'Terry'
>>> pythons
{'Chapman': 'Graham', 'Cleese': 'John', 'Idle': 'Eric',
'Jones': 'Terry', 'Palin': 'Michael', 'Gilliam': 'Terry'}
Используя один и тот же ключ ('Gilliam'), мы заменили исходное значение 'Gerry' на 'Terry'.
Помните, что ключи в словаре должны быть уникальными. Именно поэтому мы в качестве ключей использовали фамилии, а не имена — двух участников Monty Python зовут Терри. Если вы примените ключ более одного раза, победит последнее значение:
>>> some_pythons = {
... 'Graham': 'Chapman',
... 'John': 'Cleese',
... 'Eric': 'Idle',
... 'Terry': 'Gilliam',
... 'Michael': 'Palin',
... 'Terry': 'Jones',
... }
>>> some_pythons
{'Graham': 'Chapman', 'John': 'Cleese', 'Eric': 'Idle',
'Terry': 'Jones', 'Michael': 'Palin'}
Сначала мы присвоили значение 'Gilliam' ключу 'Terry', а затем заменили его на 'Jones'.
Этот вариант использования словаря — самый распространенный. Вы указываете словарь и ключ, чтобы получить соответствующее значение. Применим some_phyton из предыдущего подраздела:
>>> some_pythons['John']
'Cleese'
Если ключа в словаре нет, будет сгенерировано исключение:
>>> some_pythons['Groucho']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'Groucho'
Есть два хороших способа избежать возникновения подобного исключения. Первый — с помощью ключевого слова in проверить, имеется ли заданный ключ. Этот способ вы уже видели в предыдущем подразделе:
>>> 'Groucho' in pythons
False
Второй способ — использовать специальную функцию словаря get(). Вы указываете словарь, ключ и опциональное значение. Если ключ существует, вы получите связанное с ним значение:
>>> pythons.get('John')
'Cleese'
Если такого ключа нет, вы получите опциональное значение:
>>> pythons.get('Groucho', 'Not a Python')
'Not a Python'
В противном случае вам будет возвращен объект None (интерактивный интерпретатор ничего не выведет):
>>> pythons.get(Groucho')
>>>
Вы можете использовать функцию keys(), чтобы получить все ключи словаря. Для следующих примеров обратимся к другому словарю:
>>> signals = {'green': 'go', 'yellow': 'go faster', 'red': 'smile for the camera'}
>>> signals.keys()
dict_keys(['green', 'red', 'yellow'])
В Python 2 функция keys() возвращает простой список. В Python 3 эта функция возвращает dict_keys() — итерабельное представление ключей. Это удобно для крупных словарей, поскольку не требует времени и памяти для создания и сохранения списка, которым вы, возможно, даже не воспользуетесь. Но зачастую вам требуется именно список. В Python 3 надо вызвать функцию list(), чтобы преобразовать dict_keys в список.
>>> list(signals.keys())
['green', 'red', 'yellow']
В Python 3 вам также необходимо применять функцию list(), чтобы преобразовывать результат работы функций values() и items() в обычные списки. Я пользуюсь этой функцией в своих примерах.
Чтобы получить все значения словаря, используйте функцию values():
>>> list(signals.values())
['go', 'smile for the camera', 'go faster']
Когда вам нужно получить все пары «ключ — значение» из словаря, используйте функцию items():
>>> list(signals.items())
[('green', 'go'), ('red', 'smile for the camera'), ('yellow', 'go faster')]
Каждая пара будет возвращена в виде кортежа, например ('green','go').
Количество пар «ключ — значение» определяется так:
>>> len(signals)
3
В версиях Python 3.5 и выше есть новый способ объединять словари с помощью конструкции **, которая в главе 9 применяется совершенно по-иному:
>>> first = {'a': 'agony', 'b': 'bliss'}
>>> second = {'b': 'bagels', 'c': 'candy'}
>>> {**first, **second}
{'a': 'agony', 'b': 'bagels', 'c': 'candy'}
Фактически вы можете передать в качестве параметров больше двух словарей:
>>> third = {'d': 'donuts'}
>>> {**first, **third, **second}
{'a': 'agony', 'b': 'bagels', 'd': 'donuts', 'c': 'candy'}
Эти копии являются поверхностными. Обратитесь к подразделу, в котором рассматривается функция deepcopy() (см. подраздел «Копируем все с помощью функции deepcopy()» далее в этой главе), если вам нужно получить полные копии ключей и значений, не связанные с исходными словарями.
Вы можете использовать функцию update(), чтобы скопировать ключи и значения из одного словаря в другой.
Определим словарь pythons, содержащий имена всех участников:
>>> pythons = {
... 'Chapman': 'Graham',
... 'Cleese': 'John',
... 'Gilliam': 'Terry',
... 'Idle': 'Eric',
... 'Jones': 'Terry',
... 'Palin': 'Michael',
}
>>> pythons
{'Chapman': 'Graham', 'Cleese': 'John', 'Gilliam': 'Terry',
'Idle': 'Eric', 'Jones': 'Terry', 'Palin': 'Michael'}
Кроме того, у нас есть еще один словарь — others, содержащий имена других юмористов:
>>> others = { 'Marx': 'Groucho', 'Howard': 'Moe' }
Теперь появляется еще один программист, который решил, что члены словаря others должны быть членами Monty Python:
>>> pythons.update(others)
>>> pythons
{'Chapman': 'Graham', 'Cleese': 'John', 'Gilliam': 'Terry',
'Idle': 'Eric', 'Jones': 'Terry', 'Palin': 'Michael',
'Marx': 'Groucho', 'Howard': 'Moe'}
Что произойдет, если во втором словаре будут находиться такие же ключи, что и в первом? Победит значение из второго словаря:
>>> first = {'a': 1, 'b': 2}
>>> second = {'b': 'platypus'}
>>> first.update(second)
>>> first
{'a': 1, 'b': 'platypus'}
Код нашего анонимного программиста pythons.update(others) был корректным технически, но не фактически. Члены словаря others, несмотря на свою известность и чувство юмора, не участвовали в Monty Python. Отменим добавление последних двух элементов:
>>> del pythons['Marx']
>>> pythons
{'Chapman': 'Graham', 'Cleese': 'John', 'Gilliam': 'Terry',
'Idle': 'Eric', 'Jones': 'Terry', 'Palin': 'Michael',
'Howard': 'Moe'}
>>> del pythons['Howard']
>>> pythons
{'Chapman': 'Graham', 'Cleese': 'John', 'Gilliam': 'Terry',
'Idle': 'Eric', 'Jones': 'Terry', 'Palin': 'Michael'}
В этой функции объединены функции get() и del. Если вы передадите функции pop() ключ в качестве аргумента и такой ключ имеется в словаре, она вернет соответствующее значение и удалит пару. Если ключа в словаре нет, будет сгенерировано исключение:
>>> len(pythons)
6
>>> pythons.pop('Palin')
'Michael'
>>> len(pythons)
5
>>> pythons.pop('Palin')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'Palin'
Но если вы передадите функции pop() второй аргумент по умолчанию (как и в случае с функцией get()), словарь не изменится:
>>> pythons.pop('First', 'Hugo')
'Hugo'
>>> len(pythons)
5
Чтобы удалить все ключи и значения из словаря, вам следует использовать функцию clear() или просто переназначить заданному имени пустой словарь:
>>> pythons.clear()
>>> pythons
{}
>>> pythons = {}
>>> pythons
{}
Если вы хотите узнать, содержится ли в словаре определенный ключ, используйте ключевое слово in. Снова переопределим словарь pythons, но на этот раз пропустим имена одного-двух участников:
>>> pythons = {'Chapman': 'Graham', 'Cleese': 'John',
... 'Jones': 'Terry', 'Palin': 'Michael', 'Idle': 'Eric' }
Теперь проверим, кого мы добавили:
>>> 'Chapman' in pythons
True
>>> 'Palin' in pythons
True
На этот раз мы не забыли о Терри Гиллиаме?
>>> 'Gilliam' in pythons
False
Черт!
Как и в случае со списками, если вы внесете в словарь изменение, оно отразится на всех именах, которые на него ссылаются:
>>> signals = {'green': 'go',
... 'yellow': 'go faster',
... 'red': 'smile for the camera'}
>>> save_signals = signals
>>> signals['blue'] = 'confuse everyone'
>>> save_signals
{'green': 'go',
'yellow': 'go faster',
'red': 'smile for the camera',
'blue': 'confuse everyone'}
Чтобы скопировать ключи и значения из одного словаря в другой и запретить изменяться остальным словарям при изменении данных в одном из них, можно воспользоваться функцией copy():
>>> signals = {'green': 'go',
... 'yellow': 'go faster',
... 'red': 'smile for the camera'}
>>> original_signals = signals.copy()
>>> signals['blue'] = 'confuse everyone'
>>> signals
{'green': 'go',
'yellow': 'go faster',
'red': 'smile for the camera',
'blue': 'confuse everyone'}
>>> original_signals
{'green': 'go',
'yellow': 'go faster',
'red': 'smile for the camera'}
>>>
Эта копия является поверхностной: ее можно применять тогда, когда значения словаря неизменяемы (как в нашем случае). Если значения словаря изменяются, следует использовать функцию deepcopy().
Если бы значением по ключу red в предыдущем примере был список, а не обычная строка:
>>> signals = {'green': 'go',
... 'yellow': 'go faster',
... 'red': ['stop', 'smile']}
>>> signals_copy = signals.copy()
>>> signals
{'green': 'go',
'yellow': 'go faster',
'red': ['stop', 'smile']}
>>> signals_copy
{'green': 'go',
'yellow': 'go faster',
'red': ['stop', 'smile']}
>>>
Изменим одно из значений в списке red:
>>> signals['red'][1] = 'sweat'
>>> signals
{'green': 'go',
'yellow': 'go faster',
'red': ['stop', 'sweat']}
>>> signals_copy
{'green': 'go',
'yellow': 'go faster',
'red': ['stop', 'sweat']}
Вы видите типичную картину, когда при внесении изменений в один объект изменяются оба объекта. Метод copy() копировал значения как есть — это значит, что signal_copy получил тот же список значений для 'red', которые имел и signals.
Решением проблемы является функция deepcopy():
>>> import copy
>>> signals = {'green': 'go',
... 'yellow': 'go faster',
... 'red': ['stop', 'smile']}
>>> signals_copy = copy.deepcopy(signals)
>>> signals
{'green': 'go',
'yellow': 'go faster',
'red': ['stop', 'smile']}
>>> signals_copy
{'green': 'go',
'yellow':'go faster',
'red': ['stop', 'smile']}
>>> signals['red'][1] = 'sweat'
>>> signals
{'green': 'go',
'yellow': 'go faster',
red': ['stop', 'sweat']}
>>> signals_copy
{'green': 'go',
'yellow': 'go faster',
red': ['stop', 'smile']}
Как и в случае со списками и кортежами, рассмотренными в предыдущей главе, словари можно сравнивать с помощью операторов == и !=:
>>> a = {1:1, 2:2, 3:3}
>>> b = {3:3, 1:1, 2:2}
>>> a == b
True
Другие операторы не будут работать:
>>> a = {1:1, 2:2, 3:3}
>>> b = {3:3, 1:1, 2:2}
>>> a <= b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<=' not supported between instances of 'dict' and 'dict'
Python сравнивает ключи и значения по одному. Порядок, в котором они создавались, не имеет значения. В этом примере словари a и b равны, за исключением того, что по ключу 1 в словаре а находится список [1,2], а в словаре b — список [1,1].
>>> a = {1: [1, 2], 2: [1], 3:[1]}
>>> b = {1: [1, 1], 2: [1], 3:[1]}
>>> a == b
False
Итерирование по словарю (или его функция keys()) возвращает ключи. В этом примере в качестве ключей используются типы карт для настольной игры Clue (за пределами Северной Америки она называется Cluedo):
>>> accusation = {'room': 'ballroom', 'weapon': 'lead pipe',
... 'person': 'Col. Mustard'}
>>> for card in accusation: # или for card in accusation.keys():
... print(card)
...
room
weapon
person
Для того чтобы проитерировать не по ключам, а по значениям, используйте функцию values():
>>> for value in accusation.values():
... print(value)
...
ballroom
lead pipe
Col. Mustard
Для получения пар «ключ — значение» подходит функция items():
>>> for item in accusation.items():
... print(item)
...
('room', 'ballroom')
('weapon', 'lead pipe')
('person', 'Col. Mustard')
Присвоить значение кортежа другим переменным можно в один этап. Для каждого кортежа, возвращенного функцией items(), присвоим первый элемент кортежа (ключ) переменной card, а второй элемент (значение) — переменной contents:
>>> for card, contents in accusation.items():
... print('Card', card, 'has the contents', contents)
...
Card weapon has the contents lead pipe
Card person has the contents Col. Mustard
Card room has the contents ballroom
У словарей также существуют включения.
Простейшая форма выглядит знакомо:
{выражениедляключа : выражениедлязначения for выражение in итерабельныйобъект}
>>> word = 'letters'
>>> letter_counts = {letter: word.count(letter) for letter in word}
>>> letter_counts
{'l': 1, 'e': 2, 't': 2, 'r': 1, 's': 1}
Мы проходим в цикле по каждой из семи букв строки 'letters' и считаем, сколько раз буква встречается. Дважды использовать функцию word.count(letter) — значит впустую тратить время, поскольку нам придется по два раза посчитать буквы e и t. Но это ничему не навредит, потому что после второго вызова этой функции для буквы е мы всего лишь заменим существующую запись в словаре. То же самое верно и для буквы t. Следующий фрагмент больше соответствует «питонскому» стилю:
>>> word = 'letters'
>>> letter_counts = {letter: word.count(letter) for letter in set(word)}
>>> letter_counts
{'t': 2, 'l': 1, 'e': 2, 'r': 1, 's': 1}
Порядок ключей словаря изменился по сравнению с предыдущим примером, поскольку вызов set(word) возвращает буквы в другом порядке.
По аналогии со списковыми включениями для генератора словарей также можно использовать условные проверки if и более одного блока for:
{выражение для ключа : выражение для значения for выражение in итерабельныйобъект if условие}
>>> vowels = 'aeiou'
>>> word = 'onomatopoeia'
>>> vowel_counts = {letter: word.count(letter) for letter in set(word)
if letter in vowels}
>>> vowel_counts
{'e': 1, 'i': 1, 'o': 4, 'a': 2}
Вы сможете найти дополнительные примеры использования генераторов словарей в PEP-274 ().
Множество похоже на словарь, в котором значения отброшены, а оставлены только ключи. Как и в словаре, ключи должны быть уникальными. Вы используете множество, если хотите знать только, существует что-то или нет, а другая информация вам неважна. Это как сумка с ключами. Если же вам нужно прикрепить к ключу некую информацию, воспользуйтесь словарем.
Было время, когда теорию множеств преподавали в некоторых начальных школах наряду с основами математики. Если в вашей школе такого не было (или было, но вы в это время смотрели в окно), на рис. 8.1 можно увидеть, как объединяются и пересекаются множества.
Рис. 8.1. Обычные операции с множествами
Предположим, вы хотите объединить два множества, у которых есть несколько общих ключей. Поскольку множество должно содержать только уникальные значения, объединение двух множеств будет иметь всего лишь один такой одинаковый ключ. Пустое множество — это множество, содержащее ноль элементов. На рис. 8.1 примером пустого множества будут женские имена, начинающиеся с буквы Х.
Чтобы создать множество, воспользуйтесь функцией set(). Можно также разместить в фигурных скобках одно или несколько разделенных запятыми значений, как показано здесь:
>>> empty_set = set()
>>> empty_set
set()
>>> even_numbers = {0, 2, 4, 6, 8}
>>> even_numbers
{0, 2, 4, 6, 8}
>>> odd_numbers = {1, 3, 5, 7, 9}
>>> odd_numbers
{1, 3, 5, 7, 9}
Порядок ключей в множестве не имеет значения.
Поскольку пустые квадратные скобки [] создают пустой список, можно было бы предположить, что пустые фигурные скобки {} создают пустое множество. Вместо этого пустые фигурные скобки создают пустой словарь. Именно поэтому интерпретатор выводит пустое множество как set() вместо {}. Почему так происходит? Словари появились в Python раньше и успели захватить фигурные скобки в свое распоряжение. А владение — это девять десятых законного права.
Вы можете создать множество из списка, строки, кортежа или словаря, отбрасывая любые повторяющиеся значения.
Для начала взглянем на строку, в которой отдельные буквы встречаются несколько раз:
>>> set('letters')
{'l', 'r', 's', 't', 'e'}
Обратите внимание, что множество содержит только по одной e и t, хотя в слове letters они встречаются дважды.
Создадим множество из списка:
>>> set(['Dasher', 'Dancer', 'Prancer', 'Mason-Dixon'])
{'Dancer', 'Dasher', 'Mason-Dixon', 'Prancer'}
А теперь из кортежа:
>>> set(('Ummagumma', 'Echoes', 'Atom Heart Mother'))
{'Ummagumma', 'Atom Heart Mother', 'Echoes'}
Когда вы передаете функции set() словарь, она возвращает только ключи:
>>> set({'apple': 'red', 'orange': 'orange', 'cherry': 'red'})
{'cherry', 'orange', 'apple'}
Пересчитаем наших оленей:
>>> reindeer = set( ['Dasher', 'Dancer', 'Prancer', 'Mason-Dixon'] )
>>> len(reindeer)
4
Добавим во множество еще один элемент с помощью метода add():
>>> s = set((1,2,3))
>>> s
{1, 2, 3}
>>> s.add(4)
>>> s
{1, 2, 3, 4}
Вы можете удалить выбранное значение из множества:
>>> s = set((1,2,3))
>>> s.remove(3)
>>> s
{1, 2}
Как и в случае со словарями, вы можете проитерировать по всем элементам множества:
>>> furniture = set(('sofa', 'ottoman', 'table'))
>>> for piece in furniture:
... print(piece)
...
ottoman
table
sofa
Такое использование множеств — самое распространенное. Мы создадим словарь, который называется drinks. Каждый ключ будет названием коктейля, а соответствующее значение — множеством с ингредиентами этого напитка:
>>> drinks = {
... 'martini': {'vodka', 'vermouth'},
... 'black russian': {'vodka', 'kahlua'},
... 'white russian': {'cream', 'kahlua', 'vodka'},
... 'manhattan': {'rye', 'vermouth', 'bitters'},
... 'screwdriver': {'orange juice', 'vodka'}
... }
Хотя и словарь, и множества заключаются в фигурные скобки ({ и }), множество — это всего лишь набор значений, а словарь содержит пары «ключ — значение».
Какой из коктейлей содержит водку?
>>> for name, contents in drinks.items():
... if 'vodka' in contents:
... print(name)
...
screwdriver
martini
black russian
white russian
Мы хотим выпить коктейль с водкой, но не переносим лактозу, а вермут на вкус как керосин:
>>> for name, contents in drinks.items():
... if 'vodka' in contents and not ('vermouth' in contents or
... 'cream' in contents):
... print(name)
...
screwdriver
black russian
Перепишем этот пример чуть более кратко в следующем подразделе.
Что, если вам нужно проверить наличие сразу нескольких значений множества? Предположим, вы хотите найти какой-нибудь напиток с апельсиновым соком или вермутом. Для этого мы используем операторпересечения множеств (&):
>>> for name, contents in drinks.items():
... if contents & {'vermouth', 'orange juice'}:
... print(name)
...
screwdriver
martini
manhattan
Результатом работы оператора & является множество со всеми элементами из обоих сравниваемых списков. Если ни один из заданных ингредиентов не содержится в предлагаемых коктейлях, оператор & вернет пустое множество. Этот результат можно считать равным False.
Теперь перепишем пример из предыдущего подраздела, когда мы хотели водки, не смешанной со сливками или вермутом:
>>> for name, contents in drinks.items():
... if 'vodka' in contents and not contents & {'vermouth', 'cream'}:
... print(name)
...
screwdriver
black russian
Сохраним множества ингредиентов для этих двух напитков в переменных, чтобы не пришлось слишком много печатать в дальнейших примерах:
>>> bruss = drinks['black russian']
>>> wruss = drinks['white russian']
Ниже приведены примеры всех операторов множеств. У одних есть специальная пунктуация, у других — специальные функции, у третьих — и то и другое. Мы будем использовать тестовые множества а (содержит элементы 1 и 2) и b (содержит элементы 2 и 3):
>>> a = {1, 2}
>>> b = {2, 3}
Как вы видели ранее, пересечение (элементы, общие для обоих множеств) можно получить с помощью особого пунктуационного символа &. Функция intersection() делает то же самое:
>>> a & b
{2}
>>> a.intersection(b)
{2}
В данном фрагменте используются сохраненные нами переменные:
>>> bruss & wruss
{'kahlua', 'vodka'}
В следующем примере мы получаем объединение (члены обоих множеств), используя оператор | или функцию множества union():
>>> a | b
{1, 2, 3}
>>> a.union(b)
{1, 2, 3}
И алкогольная версия:
>>> bruss | wruss
{'cream', 'kahlua', 'vodka'}
Разность множеств (члены только первого множества, но не второго) можно получить с помощью символа — или функции difference():
>>> a – b
{1}
>>> a.difference(b)
{1}
>>> bruss – wruss
set()
>>> wruss – bruss
{'cream'}
Самыми распространенными операциями с множествами являются объединение, пересечение и разность. Для полноты картины я включил в этот подраздел и остальные операции, но, возможно, вам никогда не придется их использовать.
Для выполнения исключающего ИЛИ (элементы или первого, или второго множества, но не общие) используйте оператор ^ или функцию symmetric_difference():
>>> a ^ b
{1, 3}
>>> a.symmetric_difference(b)
{1, 3}
В этом примере определяется эксклюзивный ингредиент для русских напитков:
>>> bruss ^ wruss
{'cream'}
Проверить, является ли одно множество подмножеством другого (когда все члены первого множества являются членами второго), можно с помощью оператора <= или функции issubset():
>>> a <= b
False
>>> a.issubset(b)
False
Добавление сливок в коктейль «Черный русский» сделает его «Белым русским», поэтому wruss является подмножеством bruss:
>>> bruss <= wruss
True
Является ли любое множество подмножеством самого себя? Ага.
>>> a <= a
True
>>> a.issubset(a)
True
Для того чтобы стать собственным подмножеством, второе множество должно содержать все члены первого и несколько других. Определяется это с помощью оператора <, как показано в следующем примере:
>>> a < b
False
>>> a < a
False
>>> bruss < wruss
True
Надмножество противоположно подмножеству (все члены второго множества являются также членами первого). Для определения этого используется оператор >= или функция issuperset():
>>> a >= b
False
>>> a.issuperset(b)
False
>>> wruss >= bruss
True
Любое множество является надмножеством самого себя:
>>> a >= a
True
>>> a.issuperset(a)
True
И наконец, вы можете встретить правильное надмножество (первое множество содержит все члены второго и несколько других) с помощью оператора >, как показано здесь:
>>> a > b
False
>>> wruss > bruss
True
Множество не может быть правильным надмножеством самого себя:
>>> a > a
False
Никто не хочет оставаться в стороне, поэтому даже у множеств есть включения. Простейшая версия выглядит как включение списка или словаря, которые вы только что видели:
{ выражение for выражение in итерабельный объект}
Здесь также могут быть необязательные проверки условий:
{ выражение for выражение in итерабельный объект if условие }
>>> a_set = {number for number in range(1,6) if number % 3 == 1}
>>> a_set
{1, 4}
Если вы хотите создать множество, которое нельзя будет изменить, вызовите функцию frozenset(), передав в нее любой итерабельный аргумент:
>>> frozenset([3, 2, 1])
frozenset({1, 2, 3})
>>> frozenset(set([2, 1, 3]))
frozenset({1, 2, 3})
>>> frozenset({3, 1, 2})
frozenset({1, 2, 3})
>>> frozenset( (2, 3, 1) )
frozenset({1, 2, 3})
Достаточно ли «заморозилось» наше множество?
>>> fs = frozenset([3, 2, 1])
>>> fs
frozenset({1, 2, 3})
>>> fs.add(4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'frozenset' object has no attribute 'add'
Да, множество хорошо «заморозилось».
Напомню, структуры данных создаются следующим образом:
• список — с помощью квадратных скобок ([]);
• кортеж — с помощью запятых (и опциональных скобок);
• словарь и множество — с помощью фигурных скобок ({}).
Во всех случаях, за исключением множеств, вы получаете доступ к отдельному элементу с помощью квадратных скобок. Для списка и кортежа значение, находящееся в квадратных скобках, является целочисленным смещением. Для словаря же оно является ключом. Везде результатом будет значение. Для множества: значение либо есть, либо нет; ключей и индексов нет.
>>> marx_list = ['Groucho', 'Chico', 'Harpo']
>>> marx_tuple = 'Groucho', 'Chico', 'Harpo'
>>> marx_dict = {'Groucho': 'banjo', 'Chico': 'piano', 'Harpo': 'harp'}
>>> marx_set = {'Groucho', 'Chico', 'Harpo'}
>>> marx_list[2]
'Harpo'
>>> marx_tuple[2]
'Harpo'
>>> marx_dict['Harpo']
'harp'
>>> 'Harpo' in marx_list
True
>>> 'Harpo' in marx_tuple
True
>>> 'Harpo' in marx_dict
True
>>> 'Harpo' in marx_set
True
Ранее мы работали с простыми булевыми значениями, числами и строками. Теперь же мы работаем со списками, кортежами, множествами и словарями. Вы можете объединить эти встроенные структуры данных в свои более крупные и сложные структуры. Начнем с трех разных списков:
>>> marxes = ['Groucho', 'Chico', 'Harpo']
>>> pythons = ['Chapman', 'Cleese', 'Gilliam', 'Jones', 'Palin']
>>> stooges = ['Moe', 'Curly', 'Larry']
Мы можем создать кортеж, элементами которого станут все эти списки:
>>> tuple_of_lists = marxes, pythons, stooges
>>> tuple_of_lists
(['Groucho', 'Chico', 'Harpo'],
['Chapman', 'Cleese', 'Gilliam', 'Jones', 'Palin'],
['Moe', 'Curly', 'Larry'])
Можем также создать список, состоящий из трех списков:
>>> list_of_lists = [marxes, pythons, stooges]
>>> list_of_lists
[['Groucho', 'Chico', 'Harpo'],
['Chapman', 'Cleese', 'Gilliam', 'Jones', 'Palin'],
['Moe', 'Curly', 'Larry']]
Словарь из списков также возможен. В этом примере используем название группы комиков в качестве ключа, а список ее участников — в качестве значения:
dict_of_lists = {'Marxes': marxes, 'Pythons': pythons, 'Stooges': stooges}
>> dict_of_lists
{'Marxes': ['Groucho', 'Chico', 'Harpo'],
'Pythons': ['Chapman', 'Cleese', 'Gilliam', 'Jones', 'Palin'],
'Stooges': ['Moe', 'Curly', 'Larry']}
Ваши единственные ограничения — только сами типы данных. Например, ключи словаря должны быть неизменяемыми, поэтому списки, словари и множества не могут быть ключами для другого словаря. Но кортеж может быть. Создадим, к примеру, алфавитный указатель достопримечательностей, основываясь на GPS-координатах (широте, долготе и высоте). В главе 21 вы найдете еще несколько примеров работы с картами:
>>> houses = {
(44.79, -93.14, 285): 'My House',
(38.89, -77.03, 13): 'The White House'
}
Мы вернемся к структурам кода. Покажем, как «завернуть» код в функцию и как работать с исключениями, когда что-то идет не так.
8.1. Создайте англо-французский словарь с названием e2f и выведите его на экран. Вот ваши первые слова: dog/chien, cat/chat и walrus/morse.
8.2. Используя словарь e2f, выведите французский вариант слова walrus.
8.3. Создайте французско-английский словарь f2e на основе словаря e2f. Используйте метод items.
8.4. Используя словарь f2e, выведите английский вариант слова chien.
8.5. Выведите на экран множество английских слов из ключей словаря e2f.
8.6. Создайте многоуровневый словарь life. Используйте следующие строки для ключей верхнего уровня: 'animals', 'plants' и 'other'. Сделайте так, чтобы ключ 'animals' ссылался на другой словарь, имеющий ключи 'cats', 'octopi' и 'emus'. Сделайте так, чтобы ключ 'cats' ссылался на список строк со значениями 'Henri', 'Grumpy' и 'Lucy'. Остальные ключи должны ссылаться на пустые словари.
8.7. Выведите на экран высокоуровневые ключи словаря life.
8.8. Выведите на экран ключи life['animals'].
8.9. Выведите значения life['animals']['cats'].
8.10. Используйте генератор словаря, чтобы создать словарь squares. Используйте range(10), чтобы получить ключи. В качестве значений используйте возведенное в квадрат значение каждого ключа.
8.11. Используйте генератор множества, чтобы создать множество odd из нечетных чисел диапазона range(10).
8.12. Используйте включение генератора, чтобы вернуть строку 'Got' и числа из диапазона range(10). Итерируйте по этой конструкции с помощью цикла for.
8.13. Используйте функцию zip(), чтобы создать словарь из кортежа ключей ('optimist', 'pessimist', 'troll') и кортежа значений ('Theglassishalffull', 'Theglassishalfempty', 'Howdidyougetaglass?').
8.14. Используйте функцию zip(), чтобы создать словарь с именем movies, в котором объединены списки titles=['CreatureofHabit','CrewelFate','SharksOnaPlane'] и plots=['Anunturnsintoamonster','Ahauntedyarnshop','Checkyourexits'].
Очень похоже на финальный счет матча «Стронций» — «Углерод».
Однако, перефразируя Граучо Маркса, «я бы не хотел состоять в клубе, в который принимают таких людей, как я».