'Привет, Элис!'
>>> greeting(33333333)
KeyError: 33333333
Исключение
KeyError
— это совсем не тот результат, который мы хотели
бы видеть. Было бы намного лучше, если бы в качестве запасного варианта
функция возвращала универсальное приветствие, если идентификатор
пользователя не может быть найден.
Давайте реализуем эту идею. Наш первый подход мог бы заключаться
в простой проверке принадлежности в формате ключ в словаре (key in dict)
и возврате приветствия по умолчанию, если идентификатор пользователя
неизвестен:
def greeting(userid):
if userid in name_for_userid:
return 'Привет, %s!' % name_for_userid[userid]
else:
return 'Привет всем!'
Давайте посмотрим, как эта реализация функции
greeting()
проявит себя
с нашими предыдущими тестовыми случаями:
>>> greeting(382)
'Привет, Элис!'
>>> greeting(33333333)
'Привет всем!'
Намного лучше. Мы теперь получаем универсальное приветствие для
неизвестных пользователей, и мы поддерживаем персонализированное
приветствие, когда допустимый идентификатор пользователя найден.
Но все равно есть простор для совершенствования. Несмотря на то что эта
новая реализация дает нам ожидаемые результаты и выглядит небольшой
244 Глава 7 • Трюки со словарем
и чистой, ее все еще можно улучшить. К этому подходу у меня есть не-
сколько претензий:
он неэффективен, потому что он опрашивает словарь дважды;
он многословен, поскольку, например, часть строки с приветствием по-
вторяется;
он не является питоновским — официальная документация Python,
в частности, для таких ситуаций рекомендует использовать стиль про-
граммирования «легче попросить прощения, чем разрешения» (EAFP):
Этот общепринятый стиль программирования на Python исходно предпо-
лагает существование допустимых ключей или атрибутов и отлавливает
исключения, если предположение оказывается ложным
1
.
Более эффективная реализация, которая следует принципам EAFP, могла
бы вместо выполнения явной проверки на принадлежность ключа слова-
рю задействовать блок
try…except
, чтобы поймать исключение
KeyError
:
def greeting(userid):
try:
return 'Привет, %s!' % name_for_userid[userid]
except KeyError:
return 'Привет всем'
Эта реализация по-прежнему верна в том, что касается наших первона-
чальных требований, и теперь мы устранили необходимость опрашивать
словарь дважды.
Но мы до сих пор можем улучшить ее и предложить более чистое решение.
В словаре Python есть метод
get()
, поддерживающий параметр «по умол-
чанию», который можно использовать в качестве запасного значения
2
:
def greeting(userid):
return 'Привет, %s!' % name_for_userid.get(
userid, 'всем')
1
См. глоссарий Python «EAFP»:
https://docs .python .org/3 .6/glossary .html?highlight=glossary
2
См. документацию Python «dict.get()»:
https://docs .python .org/3/tutorial/datastructures .
html#dictionaries
7 .2 . Сортировка словарей для дела и веселья 245
Во время вызова метода
get()
он проверяет, существует ли заданный
ключ в словаре. Если это так, то возвращается значение, соответствующее
этому ключу. Если же он не существует, то вместо этого возвращается
значение по умолчанию. Как вы видите, эта реализация функции
greeting
по-прежнему работает как надо:
>>> greeting(950)
'Привет, Боб!'
>>> greeting(333333)
'Привет, всем!'
Наша заключительная реализация функции
greeting()
является сжатой,
чистой и использует средства только из стандартной библиотеки Python.
Поэтому убежден, что для этой конкретной ситуации такое решение яв-
ляется наилучшим.
Ключевые выводы
Во время проверки принадлежности ключа словарю избегайте явных
проверок в формате ключ в словаре.
Предпочтительной является обработка исключений в стиле EAFP или
использование встроенного метода
get()
.
В некоторых случаях класс
collections.defaultdict
из стандартной
библиотеки также может оказаться полезным.
7 .2 . Сортировка словарей для дела и веселья
В словарях Python нет внутренней упорядоченности ключей. Можно без
проблем выполнять обход словарей, но при этом нет никакой гарантии,
что итерация возвращает элементы словаря в каком-то определенном по-
рядке следования (хотя, начиная с Python 3.6, это и меняется).
Однако очень часто полезно получить сортированное представление
(sorted representation) словаря, поместив элементы словаря в произ-
246 Глава 7 • Трюки со словарем
вольном порядке на основе их ключа, значения или иного производного
свойства. Предположим, что у вас есть словарь
xs
со следующими парами
ключ-значение:
>>> xs = {'a': 4, 'c': 2, 'b': 3, 'd': 1}
Чтобы получить сортированный список пар ключ-значение в этом сло-
варе, вы можете применить метод
items()
словаря и затем отсортировать
результирующую последовательность на втором обходе:
>>> sorted(xs.items())
[('a', 4), ('b', 3), ('c', 2), ('d', 1)]
Кортежи ключ-значение упорядочены с использованием стандартного
лексикографического упорядочивания Python для сравнения последо-
вательностей.
Чтобы сравнить два кортежа, Python сначала сравнивает элементы, хра-
нящиеся в индексной позиции
0
. Если они различаются, то он определяет
исход сравнения. Если они равны, то сравниваются следующие два эле-
мента в индексной позиции
1
, и т. д.
Так вот, поскольку мы взяли эти кортежи из словаря, в каждом кортеже
все значения в нулевой индексной позиции, бывшие ранее ключами
словаря, являются уникальными. Поэтому здесь не придется решать про-
блемы с повторами.
В некоторых случаях лексикографическое упорядочивание может быть
именно тем, что вам нужно. В других случаях, возможно, вместо этого
стоит выполнить сортировку словаря по значению.
К счастью, есть способ взять полный контроль над тем, как упорядочи-
ваются элементы. Вы можете управлять порядком их следования путем
передачи функции ключа во встроенную функцию
sorted()
, которая из-
менит то, как будут сравниваться элементы словаря.
Функция ключа — это просто обычная функция Python, которая будет вы-
зываться с каждым элементом перед тем, как делать сравнения. Функция
ключа на входе получает элемент словаря, а на выходе возвращает требу-
емый «ключ» для сравнения порядка следования элементов.
7 .2 . Сортировка словарей для дела и веселья 247
К сожалению, слово «ключ» здесь используется в двух контекстах одно-
временно: функция ключа не касается ключей словаря, она просто отме-
чает каждый входной элемент произвольным ключом сравнения.
Теперь, возможно, нам стоит взглянуть на пример. Поверьте, понять функ-
ции ключа будет намного легче, как только вы увидите их в реальном коде.
Допустим, вы хотите получить отсортированное представление словаря
на основе его значений. Чтобы получить этот результат, вы можете исполь-
зовать следующую ниже функцию ключа, которая возвращает значение
каждой пары ключ-значение путем поиска второго элемента в кортеже:
>>> sorted(xs.items(), key=lambda x: x[1])
[('d', 1), ('c', 2), ('b', 3), ('a', 4)]
Видите, как теперь результирующий список пар ключ-значение отсортиро-
ван по значениям, хранящимся в оригинальном словаре? Чтобы осмыслить
принцип работы функции ключа, стоит потратить немного времени. Этот
мощный принцип можно применять во всех видах контекстов Python.
На самом деле этот принцип настолько распространен, что стандартная
библиотека Python включает модуль
operator
. Этот модуль реализует
Do'stlaringiz bilan baham: |