Небольшая статья о том, как можно решить одну и ту же задачу несколькими способами. Предназначена для новичков в Python и программировании.
В качестве примера взят простой случай — реализация диалога подтверждения какой-либо операции. Программа задает пользователю вопрос Вы уверены? [Д/н (Y/n)]:
, на который требуется ответить, введя одно из восьми допустимых значений (Д
, д
, Н
, н
, Y
, y
, N
, n
).
Способ №1
Первое, что приходит на ум, это реализовать проверку совпадения каждого из условий следующим образом:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
def are_you_sure1():
while True:
print("Вы уверены? [Д/н (Y/n)]: ")
response = input()
if response == "Д" or response == "д":
print("Положительный ответ: {}".format(response))
elif response == "Y" or response == "y":
print("Положительный ответ: {}".format(response))
elif response == "Н" or response == "н":
print("Отрицательный ответ: {}".format(response))
elif response == "N" or response == "n":
print("Отрицательный ответ: {}".format(response))
else:
print("Введено некорректное значение: {}".format(response)
are_you_sure1()
Можно было расписать все 8 блоков if/elif
, но для краткости используется логический оператор or
(ИЛИ) для каждой из пар возможных значений.
В этом решении нет ничего плохого и оно является правильным, хорошо характеризуя принцип «Чем проще, тем лучше». Именно к такому решению приходит большинство начинающих питонистов.
Однако, это противоречит принципу «Не повторяйся», так как любой программист стремится к уменьшению числа вводимых символов.
Способ №2
Второй способ заключается в отсечении лишних сущностей (Бритва Оккама). Не имеет значения в каком именно регистре будет введен символ в ответ на вопрос программы. Поэтому воспользуемся методом строки upper()
или lower()
для приведения символов к верхнему или нижнему регистру:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
def are_you_sure2():
while True:
print("Вы уверены? [Д/н (Y/n)]: ")
# Принимает значение, введенное пользователем и
# переводит его в верхний регистр
response = input().upper()
if response == "Д" or response == "Y":
print("Положительный ответ: {}".format(response))
elif response == "Н" or response == "N":
print("Отрицательный ответ: {}".format(response))
else:
print("Введено некорректное значение: {}".format(response))
are_you_sure2()
Введенное пользователем значение сразу же приводится к одному регистру, а далее уже происходит его проверка. Как итог — сокращение количества вводимых символов и повторяющихся блоков кода.
Способ №3
Еще один способ — проверить входит ли введенное значение в список допустимых.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
def are_you_sure3():
while True:
print("Вы уверены? [Д/н (Y/n)]: ")
response = input()
if response in ["Д", "д", "Y", "y"]:
print("Положительный ответ: {}".format(response))
elif response in ["Н", "н", "N", "n"]:
print("Отрицательный ответ: {}".format(response))
else:
print("Введено некорректное значение: {}".format(response))
are_you_sure3()
Проверка осуществляется с помощью оператора вхождения in
. Пример альтернативного мышления.
Способ №4
Еще один пример альтернативного мышления, заключающийся в использовании регулярных выражений. Для этого воспользуемся стандартным модулем для работы с регулярными выражениями re
и методом re.match()
.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import re # Импорт модуля для работы с регулярными выражениями
def are_you_sure4():
while True:
print("Вы уверены? [Д/н (Y/n)]: ")
response = input()
if re.match("[yYдД]", response):
print("Положительный ответ: {}".format(response))
elif re.match("[nNнН]", response):
print("Отрицательный ответ: {}".format(response))
else:
print("Введено некорректное значение: {}".format(response))
are_you_sure4()
Метод re.match(шаблон, строка)
ищет по заданному шаблону в начале строки. В качестве шаблона используется регулярное выражение [yYдД]
и [nNнН]
(квадратные скобки группируют символы). Более подробно тема регулярных выражений раскрывается в статьях, ссылки на которые приведены в конце. Также рекомендую книгу «Освой самостоятельно регулярные выражения. 10 минут на урок» Бена Форты.
В этом способе тоже можно использовать принцип отсечения лишнего и сократить регулярные выражения до вида [YД]
и [NН]
с помощью метода upper()
.
Также стоит отметить, что в данном случае корректными будут считаться любые значения, начинающиеся с разрешенных символов, например, да
, Yum
и т.д., так как re.match()
ищет совпадения только с начала строки. В отличии от других способов, где должно быть точное соответствие и любые лишние символы вызовут сообщение о некорректности. Можно считать это преимуществом, но ничего не мешает исправить такое поведение.
Последний способ излишен для такого простого примера, но хорош своей универсальностью и расширяемостью, так как регулярные выражения позволяют осуществлять более сложную проверку (например, проверку e-mail).
Способ №5
В комментариях random1st привел еще один изящный способ:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
def check_answer():
while True:
response = input('Вы уверены? [Д/н (Y/n)]: ')
try:
print( 0 <= "YyДдNnНн".index(response) <=3 and "True" or "False")
except ValueError:
print ("Incorrect")
check_answer()
И как справедливо заметил saboteur_kiev, этот способ изящен, но не очень красив с точки зрения поддержки кода. Если нужно добавлять новые значения будет другой разработчик — оно читается и разбирается хуже, чем решение из предыдущего комментария, где позитивный и негативный ответ явно разделены.
Ремарка
@datacompboy и MamOn указали на еще один интересный момент. Символ Y
и Н
на русскоязычной клавиатуре находятся на одной кнопке, что при невнимательности со стороны пользователя может привести к совершенно противоположным результатам. Подобные моменты тоже нужно учитывать при разработке. Поэтому в приложениях, где ошибка выбора может привести к необратимым результатам, лучше затребовать подтверждение Yes
/ No
, чтобы наверняка исключить возможность ошибки.
Ссылки
- «Чем хуже, тем лучше»
- «Не повторяйся»
- Бритва Оккама
- Pythonicway: Операторы в Python
- Pythonz.net: Метод str.upper
- Бен Форта «Освой самостоятельно регулярные выражения. 10 минут на урок»
- Типичный программист: Использование регулярных выражений в Python для новичков
- Регулярные выражения в Python: изучение и оптимизация
- Хабрахабр: Регулярные выражения, пособие для новичков
Комментарии (90)
griganton
22.05.2016 01:53про Способ №3 — если в задаче нужно искать, является ли элемент частью какого-то множества, лучше ислопьзовать set (выглядит как словарь только из ключей:
{"Y","y","Д","д"}
), а не list. Всё дело в том, что проверка на вхождение у set имеет сложность О(1), а у list — O(N) (линейно растёт с увеличением количества элементов). На четырёх элементах конечно не заметно, но чем больше элементов — тем больше будет отрыв.
Способ №4, кстати, работает некорректно, так как ответ «деревня», например, воспримется как положительный ответ, хотя должен быть некорректный. Всё дело в том, что re.match пытается сматчить начало строки, и не обязательно, если вся остальная часть строки под регулярку не подходит, re.match вернёт объект матча, который приводится к TrueZyXI
22.05.2016 02:21+2На четырёх элементах можно увидеть и обратное: проверка наличия в списке будет быстрее, чем проверка наличия в множестве. В pypy-2.6.0-r1, например,
timeit.timeit('"д" in {"Y","y","Д","д"}')
в 4—5 раз медленнееtimeit.timeit('"д" in ("Y","y","Д","д")')
иtimeit.timeit('"д" in ["Y","y","Д","д"]')
, в jython-2.7.0 — в 1,5 (список)—2,5 (кортеж) медленнее, в python-2.7.10-r1 — в 2 раза медленнее. В остальных случаях (python-3.4.3-r1, pypy3-2.4.0) медленнее(…)
/[…]
(где?то в два раза). Короче, не стоит заниматься фигнёй на пустом месте, сама проверка на наличие может и медленнее, но этот список/множество создаётся при каждом вызове функции, а создание множества затратнее создания списка. Ну и коэффициент при единице у множества и при N у списка совершенно разный.
Спекуляции вроде «у этого O(N), у того O(1)» без собственно тестов (причём, желательно, на реальном коде, а не как я проверял выше) легко могут привести к деоптимизации. Слишком много факторов: может твой список процессор нормально пихает в кэш, а множество нет. Может при 1 у множества огромный коэффициент. Может PyPy оптимизирует
x in […]
в несколько десятков тактов, аx in {…}
(из?за того, что так реже пишут) — в несколько сотен. И т.д.
Пока что по моим тестам получается, что лучше не заморачиваться и писать кортеж.
gorodnev
22.05.2016 11:47+1>> Спекуляции вроде «у этого O(N), у того O(1)»
Дело в том, что при указании сложности мы забываем про коэффициенты и «незначительные» члены, а на практике получается, что сортировка вставками работает быстрее быстрой сортировки на малых наборах данных.
Ну и про преждевременную оптимизацию, как корень всех бед тоже не стоит забывать.
griganton
22.05.2016 15:08Перед комментарием гонял тесты на 2.7.11 |Anaconda 2.5.0 (64-bit) — там способ через set и list оказались одинаковы по скорости на четырёх элементах.
Написал комментарий скорей всего про то, чтобы мышление программиста не забывало про set, чтобы использовать его для проверок на вхождение. Ведь завтра может придётся написать программу, где придётся искать элемент среди сотни или тысячи элементов.
OneManRevolution
22.05.2016 02:26Что касается 4 способа, то это фича. Я умышленно оставил такое допущение как защиту от дурака. Если пользователь попал в первую букву, то все остальное не имеет значения. Но не спорю, что в некоторых случаях стоит жестко ограничить возможность попадания лишних символов.
В любом случае спасибо за комментарии. Внесу дополнения в статью.
Andrey_Solomatin
23.05.2016 08:50Сет стоит использовать потому, что это коллекция которая логически лучше подходит под использование (x in set).
Tanner
22.05.2016 06:44+4Для меня правильный способ должен начинаться с:
from distutils.util import strtobool
conformist
22.05.2016 07:18Стараюсь обходить импорты в таких простых задачах. Тем более, что strtobool всё равно заставит написать if...else...except. На 2 строки больше получится со списком/кортежем/множеством.
Andrey_Solomatin
26.05.2016 00:17С питоном вечно так: пишешь код, а потом узнаешь что это уже есть в стандартной библиотеке :)
veveve
22.05.2016 08:52+2Если вариантов ответов очень много, можно сэкономить символов на print'ах:
while True: response = input('Вы уверены? [Д/н (Y/n)]: ') msgs = { 'yYдД': 'Положительный ответ', 'nNнН': 'Отрицательный ответ', } defalut = 'Введено некорректное значение' msg = next((v for k, v in msgs.items() if response in k), defalut) print('{}: {}'.format(msg, response))
magic4x
23.05.2016 01:19Ваш вариант принимает ответы
'Yд'
,'Nн'
и т.д. К тому же, несмотря на кажущуюся изящность предложенного решения, идет разбор словаря (окей, это py3, список ключей достается бесплатно), а сам поиск происходит в строках'yYдД'
, т.е. вроде как используется самый быстрый инструмент, но по факту ради красоты.
Чтобы два раза не вставать:
В этом способе тоже можно использовать принцип отсечения лишнего и сократить регулярные выражения до вида [YД] и [NН] с помощью метода upper().
Достаточно добавить флаг игнорирования регистра:
import re positive = ('y', 'yes', 'д', 'да', 'si', 'ja') positive_check = re.compile(r'^({})$'.format('|'.join(positive)), re.I) # вот это re.I messages = { 0: 'Отрицательный ответ', 1: 'Положительный ответ', } while True: response = input('Вы уверены? [Д/н (Y/n)]: ') choice = response or '' # Оно может быть None? match = positive_check.match(choice) msg = messages[bool(match)] print(msg) # Для любителей экономия строк и дебаггеров-экстрасенсов response = input('Вы уверены? [Д/н (Y/n)]: ') print(messages[bool(positive_check.match(response))])
random1st
22.05.2016 11:10+4def check_answer(): while True: response = input('Вы уверены? [Д/н (Y/n)]: ') try: print( 0 <= "YyДдNnНн".index(response) <=3 and "True" or "False") except ValueError: print ("Incorrect")
ValueError можно и не перехватывать, исключение вполне подходящееThePowerfulDeeZ
22.05.2016 11:49Самое красивое решение, на мой взгляд
saboteur_kiev
22.05.2016 14:16+5Плюсую за изяшное решение, но оно не самое красивое с точки зрения поддержки кода.
Если нужно добавлять новые значения будет другой разработчик — оно читается и разбирается хуже, чем решение из предыдущего комментария, где позитивный и негативный ответ явно разделены.random1st
22.05.2016 16:49+2Ох уж мне эти «писатели фреймворков для проверки ввода данных пользователем из двух значений»
random1st
22.05.2016 18:12Самое изящное предложил renskiy ИМХО.
saboteur_kiev
23.05.2016 04:13Верно, и обратите внимание — позитивный и негативный ответ — разделены ;)
Meklon
22.05.2016 11:34Вопрос начинающего питониста:
while True: там зачем? Оно же всегда верно и всегда будет выполняться. По идее можно выбросить и функция все равно будет работать.random1st
22.05.2016 16:54Это исключительно для отладки. На самом деле все решение можно вообще уложить в одну строку, просто я написал так, как проверял работу. На самом деле достаточно условия «YyДдNnНн».index(response) <=3 — это выражение будет либо True либо False либо выбрасывать ValueError, а остальное так, бутафория
datacompboy
22.05.2016 14:21+3А все замечали в таких диалогах, что «Y» и «н» — на одной кнопке, а потому в зависимости от выбранного языка одна и та же кнопка даст да либо нет?
OneManRevolution
22.05.2016 14:32Да. Например, в Ubuntu
apt
аналогичным образом запрашивает подтверждение. Если слишком критично, то лучше просить ввести полностьюYes
илиNo
, как это реализовано в некоторых консольных программах.
MamOn
22.05.2016 14:41+1Для новичков наверное ещё стоит сделать небольшую ремарочку о том, что ожидать от пользователя ввода [YyNnДдНн] это плохая идея, потому как в русской раскладке «Н» находится на одной и той же кнопке с «Y».
MamOn
22.05.2016 14:49+1Ой, наверное не стоит заваривать чай между обновлением комментариев и нажатием кнопки «Написать».
grossws
22.05.2016 15:45+1Также стоит отметить, что обычно написание одного из вариантов с заглавной буквы (
Yes
/Да
в данном случае) обычно означает, что это вариант по умолчанию и может быть выбран вводом пустой строки. Примеры в статье это не обрабатывают в текущий момент.Andrey_Solomatin
23.05.2016 08:36Так бы и написал начинающий программист :)
grossws
23.05.2016 12:34Так бы написали и опытный. Ибо негоже нарушать принцип наименьшего удивления.
FantomNotaBene
23.05.2016 12:45Тогда, если все что не является положительным ответом, считать отрицательным, это дело можно упростить до такого вида:
response = input('Вы уверены? [Д/н (Y/n)]: ').lower() print(True if response in ['y', 'д', ''] else False)
grossws
23.05.2016 12:55Вопрос не в упрощении кода (проверка
response == ''
слегка усложнит его), а в уменьшении ментальной нагрузки на пользователя программы.
Т. е. если я вижу
Upgrade packages? [y/N]
, то нажав просто энтер ожидаю, что установка будет отменена; набравt
(промазав поy
) ожидаю, что меня спросят повторно.
Если это не так (нестандартное поведение), то мне придётся специально помнить о том, что эта программа ведёт себя не по-человечески и надо вводить
y
и толькоy
. Примерно, как если бы принималась только заглавнаяn
и только прописнаяy
.FantomNotaBene
23.05.2016 14:24Полностью согласен.
def check_answer(): response = input('Вы уверены? [Д/н (Y/n)]: ').lower() if response in {'y', 'д', ''}: return True elif response in {'n', 'н'}: return False else: print('Ответом должен быть один из символов из набора [Д, д, Y, y, Н, н, N, n]') return check_answer() if check_answer(): print('Действие подтверждено') else: print('Действие отменено')
akzhan
22.05.2016 17:09-4все-таки должно быть две проверки, на наличие и корректность ввода, и уже потом на значение.
то есть по-хорошему это нечто вроде
if ( input =~ /^[YДNН]$/i ) { if ( input =~ /YД/i ) { # yes } else { # no } } else { raise ArgumentError.new( "input" ); }
random1st
22.05.2016 17:18+5Как не ставь тег «Python», обязательно придет кто-нибудь и бесцеремонно напишет в комментах на Ruby.
akzhan
22.05.2016 17:23-2Искренне недоумеваю, а какая разница? Ловите на Perl:
if ( $input =~ m/^[YДNН]$/i ) { if ( $input =~ m/YД/i ) { # yes } else { # no } } else { die "input"; }
akzhan
22.05.2016 17:34-3Вот на C++, навскидку (могут быть мелкие ошибки, не писал лет двадцать):
#include <boost/regex.hpp> using namespace boost; smatch m; if ( regex_match( input, m, "^[YyДдNnНн]$" ) { if ( regex_match( input, m, "[YyДд]" ) { // yes } else { // no } } else { throw new ArgumentError( "input" ); }
akzhan
22.05.2016 18:02+6Странные фанатики языка мне минус поставили. Знал бы Python, написал бы на нем. Но какая разница, если речь о концепциях, как обрабатывать входные данные. Язык вторичен в данном случае.
akzhan
22.05.2016 17:20-2Ну и, конечно, наверняка в Python есть аналог вот этому:
case input when /YД/i # Yes when /NН/i # No else throw ArgumentError.new "input" end
random1st
22.05.2016 17:28+2Простите, я бы предложил Вам линейку в качестве метода окончательного выяснения отношений, но, пожалуй, это может быть расценено как ребячество. Воздержусь.
akzhan
22.05.2016 17:38и вправду, ребячество. я уже посмотрел, встроенного switch в python нет, но очень легко реализуется.
random1st
22.05.2016 17:42+2Вместо switch можно использовать dict, где key — это вариант выбора, а value — функция-обработчик. Или использовать цепочку if | elif | else. Уж поверьте, в python switch просто не нужен.
akzhan
22.05.2016 17:50Ничуть не спорю. В том же Perl использование given/Switch сейчас не рекомендовано (experimental smartmatch feature).
Я хотел в первую очередь сместить акцент на то, чтобы разнести проверку на корректность значения и само значение. А оператор выбора — это по настроению.
loz
22.05.2016 18:28-4Это идиотизм, функция обработчик никак не заменит выражение в switch, если вы не передадите в нее все текущее окружение. А в elif тупо намного больше писать. Добавить switch можно в хороших языках типа лиспа и ребола, тут же речь про питон, в котором программисты без одобрения гвидо ничего сделать с языком не могут.
random1st
22.05.2016 18:49+2Без сомнения, наличие switch в языке — это критический показатель качества. Простите, а Вы правда много чего добавили в Lisp или Rebol?
loz
22.05.2016 23:01-1Наличие switch в языке — показатель того, что создатели хотябы хотят сделать его удобным для пользователей, а не откровенно посылают их куда подальше в духе жрите что дают (стоит ли вспоминать мои любимые однострочные лямбды в 2016 году?).
Я добавлял в лисп и ребол то, что мне было нужно. И важна именно возможность добавления, а не то, что добавлял конкретный программист, ведь судить о языке по опыту одного человека не очень умно, согласитесь?bromzh
23.05.2016 12:29Switch в языке высокого уровня, где всё есть объекты — излишек. Нужен либо паттерн-матчинг, как в скале, например, либо ничего.
Всё потому что switch хорошо работает только с примитивами. Свич в подавляющем количестве языков сравнивает через ==. Для чисел и строк это нормально. Для объектов он бесполезен:
class Foo: def __init__(self, bar, baz): self.bar = bar self.baz = baz a = Foo(1, 2) b = Foo(1, 2) a == b # => False
Сейчас я в основном пишу на java и js, тут есть свич. Но в коде его надобность практически отсутствует.
Лямбды не дают спать? Ну язык не поощряет длинные анонимные функции.
И как показала практика, все эти маленькие, но разумные и логически обоснованные ограничения и сделали язык таким популярным: код на питоне почти всегда легко читать и писать. В отличие от тех же лиспов, где ты хз, функция ли это, или же макрос, который изменяет синтаксис. Возможностей в лиспах много, но почему-то популярными они не становятся.
loz
23.05.2016 13:00+3Паттрен-матчинг это следующий уровень switch, но к сожалению додуматься до него смогли лишь в двух с половиной языках, и питон не один из них.
Switch на объектах все еще полезен, ведь есть возможность перегружать __eq__ (или как оно там правильно).
То, что в твоем коде не используется switch — не показатель, в примере в этом посте он, например. может быть использован.
Ну язык не поощряет длинные анонимные функции
Это откровенная неправда, Гвидо был не прочь сделать нормальные лямбды, да вот только череда провальных решений в дизайне языка не позволила сделать ничего лучше, чем то что мы получили в итоге. Как минимум многострочные лямбды не парсятся из-за того, что отступы являются частью семантики. Но теперь принято называть откровенно плохой дизайн «не поощрением».
В отличие от тех же лиспов, где ты хз, функция ли это, или же макрос, который изменяет синтаксис.
Аргумент уровня — в динамическом языке ты хз, X — это число, объект, функция или строка.
Возможностей в лиспах много, но почему-то популярными они не становятся.
Не почему-то, а именно поэтому. Возможности всегда ведут к усложнению, многие крутейшие вещи в CL существуют только в нем самом, поэтому его изучение это не просто новый синтаксис и парочка фич, а целые новые концепции и системы (например я долго ломал голову над тем как же работает condition-restart system). Поэтому программистов на лиспе в условный промежуток времени появляется меньше, чем на более простых языках, что ведет к уменьшению шансов что он будет взят на очередной проект, что уменьшает стимул его изучения и это замкнутый круг.
dna
22.05.2016 20:07+6Первым на ум приходит вариант №3 и, собственно, им всё и заканчивается. Остальные варианты уже какая-то обфускация.
yorko
23.05.2016 02:21+1По мне, вот самый простой, читаемый и «поддерживаемый» вариант.
def check_answer(): response = input('Вы уверены? [Д/н (Y/n)]: ').lower() if response in ['y', 'д']: return "True" elif response in ['n', 'н']: return "False" else: return 'Ругань'
Sna1L
23.05.2016 11:20хмм… но в Дзене Питона говорится, что лучше всего иметь один очевидный способ решения задачи…
На самом деле, не увидел в статье разного мышления. По-моему, это одно и то же решение, которое улучшается со временем. Единственное, что выделяется — решение с помощью регулярок и то… В общем, имхо способы одинаковые, никакого разного мышления здесь нет, просто лаконичность меняется.
vladbarcelo
23.05.2016 11:58+1Три вопроса:
— почему не используется кортеж вместо списка?
— почему нельзя скомбинировать .upper с кортежем и тем самым сократить длину вдвое?
— почему в примере с регекспами не используется re.IGNORECASE?
Gra4_art
23.05.2016 11:58-1Пишу MMPI(псих. тест), где для удобства обработки данных должно сохраняться только 'д' или 'н'.
Использую вариант:
answer = (((answer[0].lower()).replace('y', 'д')).replace('n', 'н'))
Chaos_Optima
23.05.2016 18:30Статья вдохновила на ненормальный кодинг сделал на С++
which(response) ( oneof('Y', 'y', 'Д', 'д'), [&] { cout << "True"; }, oneof('N', 'n', 'Н', 'н'), [&] { cout << "False"; }, always<true>(), [&] { cout << "Error"; } );
baldr
24.05.2016 21:11
toly
Если бы было больше ветвлений и/или в блоках условий было бы больше чем одна строка, я бы сделал так:
ZyXI
Если словарь всегда используется как список пар, то нафига словарь, а не список или кортеж? И у вас будет NameError (raw_input) (и AttributeError (iteritems) если вы исправите): приведённый код запускается на Python-3*: иначе ни приведённые регулярки, ни .upper() не работали бы с русским текстом.
toly
Если нужно итерировать парами значений, то я стараюсь на всякий случай использовать словарь, потому что так
прощекрасивее получить доступ к списку только первых или к списку только вторых значений.P.S. Код работает для python 2.7
ZyXI
Ага. А читающий ваш код будет думать, что вы откуда?то будете брать
("y", "Y", "д", "Д")
, а не то, что вам нужен список пар. Словарь предполагает, что основной или один из основных методов доступа —__getitem__
, а вы его не собираетесь использовать.А приведённый в статье там не работает.
toly
Сомневаюсь что читающий код начнет пристально вглядываться в actions_map вне контекста его использования.
Словарь предполагает, а программист располагает. В оригинале звучит как, программировать нужно не на языке, а с использованием языка.
Вообще, свое решение привел для того что бы:
Я не ставил целью кого-либо запутать. Я работаю с 2.7.
ZyXI
Ещё одна причина не использовать ваш вариант со словарём: в случае со списком пар вы будете использовать итератор что в Python-3*, что в 2.6+. А в случае со словарём вам нужен либо
dict.items()
, который в 2* генерирует список, а не итератор, либо 2to3 (что не удобно при разработке и вообще, по слухам там много ошибок; если нужно писать для обоих Python, лучше писать сразу на совместимом «диалекте» Python-23), либо какой?то wrapper (класс?наследник от dict, у которого items == iteritems; внешняя функция вместо метода; …).Ничего из списка не является основанием для предпочтения словаря перед списком пар.
Более структурированный вариант был бы
Основное отличие — не введение класса, а то, что incorrect_answer отделён от основных ответов, но определяется рядом, а не где?то далеко в функции. Это и без класса можно было сделать, просто я обычно предпочитаю так.
Кстати, а чего это habrahabr превращает двойные новые строки в одинарные? По PEP8 в ряде случаев предполагаются двойные.
loz
Что? Зачем делать ключами actions_map кортежи и потом еще и итерироваться по ним, когда уже есть мап? Чтобы сэкономить пару строчек когда?
toly
Не понимаю о чем речь.
Если получается без особых усилий сэкономить хотя бы одну строчку (без ущерба читаемости конечно), то я обычно экономлю.
loz
Я про то, что идея то нормальная, но я бы сделал ключами дикта сами ответы — Y, Д, N, Н. Вышло бы на пару строчек больше тут, но проще понимать и получать действие в дальнейшем, цикл будет ненужен и тд.
toly
А если понадобится поддержка китайского, будете для каждого варианта добавлять по строчке? А если вариантов 10? А я как раз упомянул, что такой код я написал бы в случае большого количества вариантов.
loz
Если будет уж очень много вариантов — я просто сгенерирую этот словарь.
werevolff
Поддерживаю данный вариант, но не совсем понятно для чего нужен словарь, если идёт перебор значений? Словарь удобен, когда мы обращаемся по key к элементу. Получается, что мы имеем N функций, отвечающих за реакцию кода на ответ пользователя. Каждая функция имеет X имён. Как присвоить X имён одной функции? Напрашивается стандартный, простой ответ: никак. Можно, конечно, изобрести способ о двух колесах и с педальками…
Второй момент: слишком много функций, которые выводят одинаковый шаблон. Предлагаю сделать вот такой простенький вариант:
MrShoor
Я бы еще лямбду убрал:
А в остальном самый лучший вариант.
werevolff
Если речь идёт о статичной строке, то да: lambda тут лишняя. Если будет использоваться format, то можно подумать. Например:
werevolff
Но опять же, lambda здесь пригодится только для повторного использования а в первом случае — если шаблоны будут отличаться по позиции строки для формата.
nickolaym
Я бы пошёл на один шаг дальше.
Не итерации по псевдо-таблице, а честную таблицу:
saboteur_kiev
как выше заметили — некоторые любят писать фреймворк для простейших задач.
А еще у вас при запуске будет
«надо думать YNynДдНн»
что не очень показательно, что это выбор ответов, а не пожелание к размышлению
nickolaym
Ну, это уже на вкус и цвет. Я особенно не заморачивался тем, как красиво отформатировать варианты.
Так-то можно
А что до фреймворка, то всё зависит от количества и способа повторений в коде.
Для однократного использования можно и самый тупой вариант наколбасить.
saboteur_kiev
И в вашем однократном примере, почему то Y заглавное, n маленькое, пустое считается за Y.
То есть стандартные ошибки программиста, который не может написать fizzbuzz? ;)
nickolaym
Это было умышленно.
Буквы «Y» и «н» находятся на одной кнопке, и — да, по умолчанию пора валить!
А для однократного использования не жалко и вручную всё наколбасить — "[Y/N], [y/n], [Д/Н], [д/н]"…