Шаг 168.
Python: тонкости программирования.
Трюки со словарем. Имитация инструкций выбора на основе словарей (окончание)

    На этом шаге мы рассмотрим еще один пример.

    Посмотрим на более законченный пример применения поиска по словарю и функций первого класса для замены цепочек инструкций 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.

    На следующем шаге мы подитожим изученный материал.




Предыдущий шаг Содержание Следующий шаг