Однажды мой друг разместил сообщение на своем Plurk о стиле написания оператора if/else. Дело в том, что он хочет знать, как элегантно использовать оператор if/else вместо оператора if/else if/else. Например, если мы хотим реализовать другой оператор на основе определенного элемента файла JSON.

Here is the json file looks like.
{
    "ops": "add",
    "value_1": 1,
    "value_2": 2
},
{
    "ops": "sub",
    "value_1": 1,
    "value_2": 2
},
{
    "ops": "mul",
    "value_1": 1,
    "value_2": 2
},
{
    "ops": "div",
    "value_1": 1,
    "value_2": 2
}

Вот оригинальный стиль кода моего друга.

if data["ops"] == "add":
    ans = data["value_1"] + data["value_2"]
elif data["ops"] == "sub":
    ans = data["value_1"] - data["value_2"]
elif data["ops"] == "mul":
    ans = data["value_1"] * data["value_2"]
elif data["ops"] == "div":
    ans = data["value_1"] / data["value_2"]
else:
    # pass

И у меня другое мнение об этом коде. Я думаю, что «элиф» немного «уродлив», почему бы вам просто не использовать оператор «если»? Потому что нам нужно иметь дело только с той операцией, которую мы предоставили. Итак, код выглядит так.

if data["ops"] == "add":
    ans = data["value_1"] + data["value_2"]
if data["ops"] == "sub":
    ans = data["value_1"] - data["value_2"]
if data["ops"] == "mul":
    ans = data["value_1"] * data["value_2"]
if data["ops"] == "div":
    ans = data["value_1"] / data["value_2"]

Но через некоторое время у меня появилась другая идея: почему я просто использую лямбда-выражение и словарь для создания карты ключ-значение для конкретного оператора? Итак, код выглядит так.

operators = {
    "add": lambda x: x["value_1"] + x["value_2"],
    "sub": lambda x: x["value_1"] - x["value_2"],
    "mul": lambda x: x["value_1"] * x["value_2"],
    "div": lambda x: x["value_1"] / x["value_2"]
}
ans = operators[data["ops"]](data)

После того, как я представил свою новую идею моему другу, и он сказал, как насчет еще части? Что, если данные содержат оператор, которого нет в элементе «ops»? Да, мой код не сработает. И я сказал, используйте try/except, чтобы перехватывать исключения и справляться с ними.

try:
    ans = operators[data["ops"]](data)
except KeyError:
    print("ops: {ops} not support".format(ops=data["ops"]))

Но как насчет того, чтобы мы действительно захотели добавить оператор if/else в наш код, сказал мой друг. Что ж, сделаем!

if data["ops"] in operators.keys():
    ans = operators[data["ops"]](data)
else:
    print("ops: {ops} not support".format(ops=data["ops"]))

Давайте сравним производительность этих реализаций! Код доступен в github.

time_if_else_statement: 22.46713638305664 ms
time_if_elif_statement: 17.323017120361328 ms
time_try_except_dict_func_statement: 24.687767028808594 ms
time_if_else_dict_func_statement: 28.36775779724121 ms

Результаты немного взорвали мой разум. Оператор if/elif работает намного быстрее, чем другие, когда мы устанавливаем количество тестовых случаев равным 50000. Это своего рода компромисс между производительностью и читаемостью кода. Для меня? Я выберу try_except_dict_func_statement для своего кода в похожей ситуации. Легко понять и поддерживать.