Вопрос:
Я изучаю Python (и программирование в целом), создавая небольшие программы. Ниже приведена базовая программа покупок, которая вернет список предметов для покупки на основе выбранной пищи.
Я хотел бы улучшить его и позволить пользователю выбирать сразу несколько продуктов (например, пользовательский ввод будет “1, 2, 3” ) и вернет список ингредиентов на основе этого.
Какой подход я должен принять? Я использую Python 2.7, и вот мой существующий код:
mm_soup = [‘minced meat’, ‘potatoes’, ‘frozen vegetable’] sunday_soup = [‘chicken with bones’, ‘noodles’, ‘soup vegetable’] gulas = [‘pork meat’, ‘food cream’, ‘potatoes’, ‘onion’, ‘frozen peas’] print «What would you like to cook on weekend?» print «Here are the options:» print «1. Minced Meat Soup» print «2. Sunday Soup» print «3. Gulas» choose = raw_input(«> «) if choose == ‘1’: print «Buy», «, «.join(mm_soup) + «.» elif choose == ‘2’: print «Buy», «, «.join(sunday_soup) + «.» elif choose == ‘3’: print «Buy», «, «.join(gulas) + «.» else: print «Hmmm. No such food on the list.» Лучший ответ:
Есть некоторые общие проблемы с вашим кодом, поэтому начните с их исправления.
У вас есть несколько элементов, которые вы хотите представить у пользователя, и вы строго кодируете эти значения. Это прикладывает для вас много усилий, поскольку вам приходится много повторять. Посмотрите на свои линии выбора, все они приходят в одно и то же. Вы также повторяете себя, определяя, на что ссылаются числа в вашем описании и в вашем коде. Попробуйте упростить это с помощью структуры данных.
Здесь мы составляем список всех опций – список кортежей, определяющий имя и набор элементов для данного питания. Мы используем набор здесь, так как нам не нужно заказывать элементы.
options = [ («Minced Meat Soup», {‘minced meat’, ‘potatoes’, ‘frozen vegetable’}), («Sunday Soup», {‘chicken with bones’, ‘noodles’, ‘soup vegetable’}), («Gulas», {‘pork meat’, ‘food cream’, ‘potatoes’, ‘onion’, ‘frozen peas’}), ]
Это дает нам хорошую структуру данных для начала.
Затем мы можем сформулировать наш вопрос, а не вручную его строить, мы можем построить его из нашего списка опций, используя цикл:
print «What would you like to cook on weekend?» print «Here are the options:» for option, (name, values) in enumerate(options, 1): print str(option)+». «+name
Обратите внимание на использование enumerate() builtin, чтобы предоставить нам номера для параметров. Поскольку вы хотите начать с 1, и Python рассчитывает от 0 нормально, мы также передаем это.
Это дает нам наш результат, но теперь мы можем легко добавить больше элементов без изменения существующего кода. Мы спрашиваем, как раньше, а затем вместо нагрузки if/elif s, мы можем просто получить указатель, который они дают нам из списка. Сначала мы должны изменить строку на число, а затем отнять один (как Python рассчитывает от 0). Это дает нам:
_, values = options[int(choose)-1]
(используя распаковку кортежей, чтобы игнорировать первое значение, так как это имя, которое нам не нужно).
Единственная проблема теперь в том, что произойдет, если пользователь вводит, например, число за пределами допустимого диапазона или вместо слова. Вы можете проверить это, прежде чем конвертировать в int и использовать его, но Pythonic просто попробуйте его и поймать заброшенные исключения при неудаче. Например:
try: _, values = options[int(choose)-1] print «Buy», «, «.join(values) + «.» except (IndexError, ValueError): print «Hmmm. No such food on the list.»
Это делает всю программу намного меньше, а также замечает, как легко добавлять новые элементы, вы просто добавляете их в список.
Итак, как мы имеем дело с несколькими элементами? Ну, это тоже довольно просто. Мы можем взять пользовательский ввод, разделить на запятые и пометить значения для удаления любых пробелов, а затем сделать то же самое, что и раньше:
for choice in choose.split(«,»): choice = choice.strip() try: _, values = options[int(choice)-1] print «Buy», «, «.join(values) + «.» except (IndexError, ValueError): print «Hmmm. No such food on the list.»
Это работает, распечатывая несколько строк для покупок, но это не оптимально, лучше было бы создать один более крупный список покупок, содержащий все необходимые элементы.
Мы можем построить это, создав набор всех элементов по мере цикла, а затем распечатав этот набор.
shopping_list = [] for choice in choose.split(«,»): choice = choice.strip() try: _, values = options[int(choice)-1] shopping_list.append(values) except (IndexError, ValueError): print «Hmmm. No such food on the list.»
Это немного неэффективно и уродливо. У Python есть встроенные функции для создания списков – подсчет списков. Мы можем сделать эту операцию следующим образом:
try: shopping_list = [options[int(choice.strip())-1][3] for choice in choose.split(«,»)] except (IndexError, ValueError): print «Hmmm. No such food on the list.»
Теперь нам нужно распечатать все значения в списке. Помните, что это список наборов, поэтому «, «.join() не будет делать то, что мы хотим. У нас есть два варианта. Мы можем либо использовать выражение генератора, чтобы сначала присоединиться к наборам, а затем присоединить присоединенные строки:
print «Buy » + «, «.join(«, «.join(values) for values in shopping_list) + «.»
Или, мы могли бы использовать itertools.chain.from_iterable(), чтобы вернуть сплющенный итератор:
print «Buy » + «, «.join(itertools.chain.from_iterable(shopping_list)) + «.»
Это дает нам:
import itertools options = [ («Minced Meat Soup», {‘minced meat’, ‘potatoes’, ‘frozen vegetable’}), («Sunday Soup», {‘chicken with bones’, ‘noodles’, ‘soup vegetable’}), («Gulas», {‘pork meat’, ‘food cream’, ‘potatoes’, ‘onion’, ‘frozen peas’}), ] print «What would you like to cook on weekend?» print «Here are the options:» for option, (name, values) in enumerate(options, 1): print str(option)+». «+name choose = raw_input(«> «) try: shopping_list = [options[int(choice.strip())-1][1] for choice in choose.split(«,»)] print «Buy » + «, «.join(itertools.chain.from_iterable(shopping_list)) + «.» except (IndexError, ValueError): print «Hmmm. No such food on the list.»
Что производит что-то вроде:
What would you like to cook on weekend? Here are the options: 1. Minced Meat Soup 2. Sunday Soup 3. Gulas > 1, 2 Buy potatoes, frozen vegetable, minced meat, chicken with bones, noodles, soup vegetable.
Это короткий, легко расширяемый и хорошо функционирующий. Есть еще некоторые проблемы, с которыми вы могли бы столкнуться (как вы хотите обрабатывать несколько элементов одного и того же элемента? Возможно, вы захотите изучить collections.Counter для этого), но это основная идея.
Ответ №1
Я полагаю, вы могли бы просто разбить введенную строку и выполнить итерацию по ее элементам:
choose = raw_input(«> «) for choice in choose.split(‘, ‘): #split by comma and space if choice == ‘1’: print «Buy», «, «.join(mm_soup) + «.» elif choice == ‘2’: print «Buy», «, «.join(sunday_soup) + «.» elif choice == ‘3’: print «Buy», «, «.join(gulas) + «.» else: print «Hmmm. No such food on the list.»
Или что-то в этом роде:
choose = raw_input(«> «); selected = choose.split(‘, ‘) #split by comma and space suggestion = »; if ‘1’ in selected : suggestion += «, «.join(mm_soup) if ‘2’ in selected : suggestion += «, «.join(sunday_soup) if ‘3’ in selected : suggestion += «, «.join(gulas) if len(suggestion) > 0: print «Buy » + suggestion + «.» else: print «Hmmm. No such food on the list.»