Автоматизируйте скучную работу Глава 8 Сэндвичница

В настоящее время я изучаю Python, работая над Автоматизируйте скучную работу с Python. Этот проект от Глава 8 который ориентирован на проверку ввода с помощью модуля PyInputPlus. Когда я заканчиваю, мне нравится искать проблему и сравнивать ее с решениями других, чтобы увидеть, что я мог бы сделать лучше. Однако я могу найти только пару ссылок на эту проблему. Вот в чем проблема:

Изготовитель сэндвичей

Напишите программу, которая запрашивает у пользователей их предпочтения по сэндвичу. Программа должна использовать PyInputPlus, чтобы убедиться, что они вводят допустимые данные, например:

  • С помощью inputMenu() для сорта хлеба: пшеничный, белый или на закваске.
  • С помощью inputMenu() для протеина: курица, индейка, ветчина или тофу.
  • С помощью inputYesNo() спросить, не хотят ли они сыра. Если да, используя inputMenu() попросить сыр: чеддер, швейцарский или моцарелла.
  • С помощью inputYesNo() спросить, хотят ли они майонеза, горчицы, салата или помидоров.
  • С помощью inputInt() спросить, сколько бутербродов они хотят. Убедитесь, что это число равно 1 или больше.

Придумайте цены для каждого из этих вариантов, и пусть ваша программа отображает общую стоимость после того, как пользователь вводит свой выбор.

При этом я хотел бы услышать, что я могу сделать лучше и как я могу улучшить этот код:

import pyinputplus as pyip

# store a dictionary of ingredients and their respective prices
optionPrices = {'white' : 2.00,
                'wheat' : 2.50,
                'sour dough' : 3.00,
                'chicken' : 2.50,
                'turkey' : 2.25,
                'ham' : 1.75,
                'tofu' : 4.00,
                'cheddar' : 1.00,
                'swiss' : 1.25,
                'mozzarella' : 2.00,
                'mayo' : 0.25,
                'mustard' : 0.25,
                'lettuce' : 0.30,
                'tomato' : 0.50
                }

customerOrder = [] # a list to store the current order
extras = ['mayo', 'mustard', 'lettuce', 'tomato']
sandwichTotal = 0.0

# ask the user for bread choice and append to order list
breadChoice = pyip.inputMenu(['white', 'wheat', 'sour dough'], 'Please choose your bread:n', lettered=True)
customerOrder.append(breadChoice)

# ask the user for protein choice and append to order list
proteinChoice = pyip.inputMenu(['chicken', 'turkey', 'ham', 'tofu'], 'Please choose your protein:n', lettered=True)
customerOrder.append(proteinChoice)

# ask if the user wants cheese, and if so, record cheese choice
cheeseResponse = pyip.inputYesNo('Would you like cheese?n')
if cheeseResponse == 'yes':
    cheeseChoice = pyip.inputMenu(['cheddar', 'swiss', 'mozzarella'], 'Please choose your cheese:n', lettered=True)
    customerOrder.append(cheeseChoice)
else:
    cheeseChoice=""

# loop through 'extras' and ask if customer wants each one. If so, append it the order
choice=""
for i in extras:
    choice = pyip.inputYesNo('Would you like ' + i +'?n')
    if choice == 'yes':
        customerOrder.append(i)
    else:
        choice=""

# get the number of sandwiches from the customer
numSandwiches = pyip.inputInt('How many sandwiches would you like?n', min=1)

print('nYour order: ')
# check if the item exists in the options, and get the price for each sandwich
for item in customerOrder:
    if item in optionPrices.keys():
        sandwichTotal += optionPrices.get(item)
        print('t' + item + ' - $' + str(optionPrices.get(item)))

print('Total for your sandwich: $' + str('{:0.2f}'.format(sandwichTotal))) # per sandwhich total
print('Total for your order: (' + str(numSandwiches) + ' sandwiches @ $' + 
      str('{:0.2f}'.format(sandwichTotal)) + ' each): ')
print('$' + str('{:0.2f}'.format(sandwichTotal * numSandwiches))) # give the total price of sandwiches

Я знаю, что, вероятно, есть много вещей, которые я мог бы сделать лучше для оптимизации или облегчения чтения кода, но есть некоторые вещи, которым я просто еще не научился. Я понимаю, что это, вероятно, не самый эффективный способ решения этой проблемы, и поэтому я хотел бы получить обратную связь.

2 ответа
2

PEP8

PEP 8 – «Руководство по стилю кода Python» содержит множество рекомендаций по написанию программ для максимального понимания между программистами.

Имена переменных

Тот, который вы нарушаете больше всего, относится к именованию переменных. PEP8 рекомендует snake_case для всех переменных. Так customerOrder должно быть customer_order, sandwichTotal должно быть sandwich_total, так далее.

Константы

Константы, которые никогда не меняются, следует называть с использованием UPPERCASE или UPPERCASE_WITH_UNDERSCORES. Следовательно, optionPrices должно быть OPTION_PRICES.

Белое пространство

PEP-8 рекомендует не допускать пробелов между клавишей дикции и :.

Инкапсулировать данные в контейнеры

optionPrices содержит хлеб, белки, сыры и другие добавки. Похоже, что это слишком много набивки – ну, начинки – в одну емкость.

Я мог бы написать:

BREAD_PRICE = {'white':2.00, 'wheat': 2.50, 'sour dough': 3.00}
PROTEIN_PRICE = {'chicken': 2.50, 'turkey': 2.25, 'ham': 1.75, 'tofu': 4.00}
CHEESE_PRICE = {'cheddar': 1.00, 'swiss' : 1.25, 'mozzarella' : 2.00}
EXTRA_PRICE = {'mayo': 0.25, 'mustard': 0.25, 'lettuce': 0.30, 'tomato': 0.50}

С вариантами хлеба, протеина и сыра теперь вы можете написать общую функцию:

def get_sandwich_choice(category, options):
    prompt = "Please choose your " + category + ":n"
    choices = list(options.keys())
    choice = pyip.inputMenu(choices, prompt, lettered=True)
    return choice

bread_choice = get_sandwich_choice('bread', BREAD_PRICE)
protein_choice = get_sandwich_choice('protein', PROTEIN_PRICE)
...

Контейнеры с ценами – это действительно одни и те же вещи, только группами. Так что вместо этого можно …

PRICE = {'bread': {'white':2.00, 'wheat': 2.50, 'sour dough': 3.00},
         'protein': {'chicken': 2.50, 'turkey': 2.25, 'ham': 1.75, 'tofu': 4.00},
         'cheese': {'cheddar': 1.00, 'swiss' : 1.25, 'mozzarella' : 2.00},
         'extras': {'mayo': 0.25, 'mustard': 0.25, 'lettuce': 0.30, 'tomato': 0.50},
         }

и варианты выбора можно было получить с помощью:

bread_choice = get_sandwich_choice('bread', PRICE['bread'])
protein_choice = get_sandwich_choice('protein', PRICE['protein'])
...

Вы можете расширить его, добавив больше данных. bread выбор, protein это выбор, cheese необязательный выбор, extras являются необязательными. В зависимости от типа категории вы можете вызывать соответствующие функции пользовательского ввода. Приглашение для каждой категории можно настроить и так далее. Чтобы добавить эти дополнительные данные, я бы посмотрел на @dataclass.

Все вещи в customerOrder тоже самое? Хлеб по-своему уникален; вам понадобится два ломтика для бутерброда. У статистов в конечном итоге могут быть варианты, такие как «легкий майонез» или «тяжелый прием горчицы», когда вы не можете попросить дополнительный хлеб. Так что, возможно, имеет смысл держать их отдельно. bread_choice, protein_choice, cheese_choice, и список extras. Это тоже помогает нам в стоимости, так как цены указаны в разных контейнерах.

def sandwich_cost(bread, protein, cheese, extras):
    cost = PRICE['bread'][bread] + PRICE['protein'][protein]
    if cheese:
        cost += PRICE['cheese'][cheese]
    for extra in extras:
        cost += PRICE['extra'][extra]

sandwich_total = sandwich_cost(bread_choice, protein_choice, cheese_choice, extras)

Опять же @dataclass может пригодиться при строительстве сэндвич-объекта.

Имея отдельную функцию для вычисления стоимости сэндвича, вы делаете свой код тестируемым. Например, вы можете проверить:

assert sandwich_cost('white', 'tofu', 'cheddar', ['tomato']) == 7.50
assert sandwich_cost('white', 'tofu', '', []) == 6.00

Смотреть в unittest для лучших способов написания тестового кода.

Главный гвардеец

Это хорошая привычка переносить ваш код в функции и вызывать его изнутри «основной защиты» в конце кода. Например)

import pyinputplus as pyip

... helper functions here ...

def main():
    ... code here ...

if __name__ == '__main__':
    main()

Резюме

Множество способов улучшить этот код. Я коснулся нескольких. Поэкспериментируйте, и, когда у вас что-то получится, задайте новый вопрос для получения дополнительных рекомендаций.

  • Большое вам спасибо за то, что нашли время сломать это и помочь мне.

    – tylerc515


Ненужные else-утверждения

В

if cheeseResponse == 'yes':
    cheeseChoice = pyip.inputMenu(['cheddar', 'swiss', 'mozzarella'], 'Please choose your cheese:n', lettered=True)
    customerOrder.append(cheeseChoice)
else:
    cheeseChoice=""

последний оператор else не требуется. cheeseChoice используется только когда cheeseResponse является yes. Следовательно, нет необходимости присваивать ему значение, если cheeseResponse является no.

поскольку cheeseChoice используется только один раз для добавления выбора в список порядка, вы можете полностью отбросить переменную и встроить ее в добавление. Однако если явное объявление cheeseChoice поможет вам прочитать и понять код, тогда его можно оставить там. В конце концов, есть хороший аргумент в пользу того, что удобочитаемость является наиболее важным свойством кода (помимо, конечно, правильной работы).

Если строка кода становится слишком длинной, вы также можете перенести ее на следующую строку, чтобы сделать ее более читаемой. Я использовал явный разрыв строки () Вот. Более подробную информацию можно найти в этом вопросе.

if cheeseResponse == 'yes':
    customerOrder.append(pyip.inputMenu(['cheddar', 'swiss', 'mozzarella'], 
            'Please choose your cheese:n', lettered=True))

Соответственно в

choice=""
for i in extras:
    choice = pyip.inputYesNo('Would you like ' + i +'?n')
    if choice == 'yes':
        customerOrder.append(i)
    else:
        choice=""

оператор else не нужен. Однако первоначальное объявление choice (поскольку затем он переназначается внутри цикла for. В цикле for вы можете использовать более подробную переменную вместо i который обычно используется при увеличении int-counter в чем-то вроде for i in range(10):. Лично я предпочитаю for extra in extras: Вот.

for extra in extras:
    choice = pyip.inputYesNo('Would you like ' + extra +'?n')
    if choice == 'yes':
        customerOrder.append(extra)

или встроенный:

for extra in extras:
    if pyip.inputYesNo('Would you like ' + extra +'?n') == 'yes':
        customerOrder.append(extra)

Удачи в ваших усилиях.

  • Спасибо за дополнительную информацию. Я сейчас переписываю, используя эти советы.

    – tylerc515

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *