На этом шаге мы рассмотрим еще один пример.
Посмотрим на более законченный пример применения поиска по словарю и функций первого класса для замены цепочек инструкций if. После ознакомления с приведенным ниже примером вы сможете увидеть шаблон, необходимый для сведения определенных видов инструкций if к диспетчеризации на основе словаря.
Мы собираемся написать еще одну функцию с цепочкой инструкций if, которую затем преобразуем. Данная функция принимает строковый код операции, к примеру "add" или "mul", и затем выполняет соответствующие математические расчеты на операндах x и у:
>>> def dispatch_if(operator, x, y): if operator == 'add': return x + y elif operator == 'sub': return x - y elif operator == 'mul': return x * y elif operator == 'div': return x / y
Сказать по правде, это очередной игрушечный пример (не хотелось бы вам здесь докучать нескончаемыми страницами исходного кода), но он будет служить наглядной иллюстрацией лежащего в основе шаблона проектирования. Как только вы "въедете" в образец, то сможете его применять в самых разных сценариях.
Вы можете испытать функцию dispatch_if() на предмет выполнения простых вычислений, вызвав эту функцию со строковым кодом операции и двумя числовыми операндами:
>>> dispatch_if('mul', 2, 8) 16 >>> dispatch_if('неизвестно', 2, 8) None
Обратите внимание на то, что случай срабатывает, потому что Python добавляет в конец любой функции неявную инструкцию return None.
Пока все неплохо. Теперь преобразуем первоначальную функцию dispatch_ if() в новую функцию, использующую словарь для отображения кодов операций в арифметические операции с функциями первого класса:
>>> def dispatch_dict(operator, x, y): return { 'add': lambda: x + y, 'sub': lambda: x - y, 'mul': lambda: x * y, 'div': lambda: x / y, }.get(operator, lambda: None)()
Такая реализация на основе словаря дает те же самые результаты, что и первоначальная функция dispatch_if(). Мы можем вызвать обе функции точно таким же образом:
>>> dispatch_dict('mul', 2, 8) 16 >>> dispatch_dict('неизвестно', 2, 8) None
Есть пара способов, которыми этот код можно усовершенствовать еще больше, если бы он был реален и предназначался для эксплуатации.
Во-первых, всякий раз, когда мы вызываем dispatch_dict(), он создает временный словарь и кучу лямбд для поиска кода операции. С точки зрения производительности это не идеально. В случае, если программный код нуждаетется в быстродействии, имеет больше смысла единожды создать словарь в качестве константы и затем ссылаться на него во время вызова функции. Не стоит воссоздавать словарь всякий раз, когда мы должны выполнить по нему поиск.
Во-вторых, если бы мы и правда захотели выполнить несколько простых арифметических операций типа x + y, то вместо используемых в этом примере лямбда-функций было бы гораздо лучше использовать встроенный модуль Python operator. Модуль operator предоставляет реализации всех операторов Python, в частности operator.mul, operator.div и т. д. Хотя эта деталь малозначительна. В этом примере лямбды использованы намеренно, чтобы сделать его более универсальным. Он должен помочь вам применять этот шаблон и в других ситуациях.
Итак, теперь у вас есть еще один инструмент в наборе хитрых приемов, который вы можете использовать для упрощения некоторых цепочек инструкций if на случай, если они будут становиться громоздкими. Просто запомните: этот прием применим не во всех ситуациях, и иногда будет лучше обойтись простой инструкцией if.
На следующем шаге мы подитожим изученный материал.