Однажды мой друг разместил сообщение на своем 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 для своего кода в похожей ситуации. Легко понять и поддерживать.