这只是一种简单的练习来触及我学到的一些东西。我还试图考虑到Pep 8格式实践,以我的能力和适用的意见,从我以前的帖子。很难将凹痕保持在2级以内。建设性的批评很受赞赏。
main.py
from time import sleep
import sys
from resources import MENU, resources
ACTIONS = ['espresso', 'latte', 'cappuccino', 'report', 'off', 'e', 'l', 'c', 'r', 'o']
FORMAT = '.2f'
TIME = 1.5
def perform_action(choice):
""" Main loop for paying for and receiving a coffee type"""
if choice in ['espresso', 'e']:
choice = 'espresso'
payment = paying(choice)
prepare(choice, payment)
elif choice in ['latte', 'l']:
choice = 'latte'
payment = paying(choice)
prepare(choice, payment)
elif choice in ['cappuccino', 'c']:
choice = 'cappuccino'
payment = paying(choice)
prepare(choice, payment)
elif choice in ['report', 'r']:
for item in resources:
print(f'{item.title()}: {resources[item] if item != "money" else f"${format(resources[item], FORMAT)}"}\n')
else:
sys.exit()
def paying(drink):
""" Checks for payment and its viability and returns how much you paid"""
cost = MENU[drink]['cost']
quarter, dime, nickel, penny = 'None', 'None', 'None', 'None'
while not quarter.isdigit():
quarter = (input('How many quarters? '))
while not dime.isdigit():
dime = (input('How many dimes? '))
while not nickel.isdigit():
nickel = (input('How many nickels? '))
while not penny.isdigit():
penny = (input('How many pennies? '))
quarter, dime, nickel, penny = int(quarter) * .25, int(dime) * .10, int(nickel) * .05, int(penny) * .01
pay = (quarter + dime + nickel + penny)
if pay < cost:
print(f'Not enough payment!, refunding ${pay}...\n')
sleep(TIME)
return [False,pay]
elif pay > cost:
change = pay - cost
pay = pay - change
print(f'Returning change of ${format(change, FORMAT)}...\n')
sleep(TIME)
print(f'Payment Successful!, ordering {drink}...\n')
sleep(TIME)
resources['money'] += pay
return [True, pay]
def prepare(drink, pay):
"""Calculates and removes resources from coffee machine if drink resources don't exceed it."""
prepared = pay[0]
if prepared:
for i in resources:
if i == 'money':
continue
if drink == 'espresso' and i == 'milk':
continue
if resources[i] < MENU[drink]['ingredients'][i]:
prepared = False
print(f'Not enough {i}.')
if prepared:
resources[i] -= MENU[drink]['ingredients'][i]
if prepared:
print(f'Enjoy your {drink}!\n')
sleep(TIME)
else:
print(f'Refunding {pay[1]}....')
resources['money'] -= pay[1]
running = True
while running:
action = None
while action not in ACTIONS:
action = input(
"\nCoffee:\n\t⚪Espresso[E]: $1.50\n\t⚪Latte[L]: $2.50\n\t⚪Cappuccino[C]: "
"$3.00\n\nCommands:\n\t⚪Report[R]\n\t⚪Off[O]\n\n"
"What would you like? "
).lower()
print('\n')
perform_action(action)资源.
MENU = {
"espresso": {
"ingredients": {
"water": 50,
"coffee": 18,
},
"cost": 1.5,
},
"latte": {
"ingredients": {
"water": 200,
"milk": 150,
"coffee": 24,
},
"cost": 2.5,
},
"cappuccino": {
"ingredients": {
"water": 250,
"milk": 100,
"coffee": 24,
},
"cost": 3.0,
}
}
resources = {
"water": 300,
"milk": 200,
"coffee": 100,
"money": 0
}发布于 2023-06-01 01:27:51
对于初学者来说,这是个不错的开始。
ACTIONS不是很好地表示为字符串列表。相反,可以考虑使用字符串到操作函数的字典。通过使用partial,可以将动作函数预绑定到参数(如饮料产品)。
perform_action中有重复的代码。您可以通过维护一个简单的产品及其价格数据库来减少这种重复。
类似地,paying中也有重复的代码(应该称为pay,相关的祈使时态动词)。考虑循环处理一系列硬币定义。
不要sleep。这是一个UI反功能,我通常认为它是在欺骗你的用户(看起来这个程序在做一些值得等待的事情,而不是)。
FORMAT并不像硬编码常量那样有用。在简单的情况下,我只需要在使用.2f的地方编写它;但是更容易移植的事情是使用来自locale模块的货币格式支持。
sys.exit()在perform_action内部调用不是件好事(实际上,在这个程序中的任何地方)。您应该使用return来优雅地展开堆栈,而不是退出。
您的变量quarter等从字符串开始,最后作为浮动结束。不要在使用过程中改变变量的类型。
将PEP484类型提示添加到函数签名中。
将从running = True开始的主循环移到main()函数中,以便这些变量不是全局变量,这样单元测试人员就可以有选择地导入某些函数,同时避免主入口点。出于这个原因,还添加了一个if name == '__main__'守卫。
resources.py作为JSON文件比作为代码更好。
if drink == 'espresso' and i == 'milk'不应该是硬编码的。只要循环一下资源。
一些中等水平的东西,但没有什么是你学不到的。
资源.
[
{
"name": "espresso",
"cost": 1.50,
"ingredients": {
"water": 50,
"coffee": 18
}
},
{
"name": "latte",
"cost": 2.50,
"ingredients": {
"water": 200,
"milk": 150,
"coffee": 24
}
},
{
"name": "cappuccino",
"cost": 3.00,
"ingredients": {
"water": 250,
"milk": 100,
"coffee": 24
}
}
]main.py
import json
from functools import partial
from locale import currency, setlocale, LC_ALL
from typing import Callable, Iterator, Any, NamedTuple
resources = {
"water": 300,
"milk": 200,
"coffee": 100,
"money": 0
}
class Denomination(NamedTuple):
plural: str
cents: int
def input_tender(self) -> int:
count = int(input(f'How many {self.plural}? '))
if count < 0:
raise ValueError('Cannot tender a negative coin count')
return count * self.cents
DENOMINATIONS = (
Denomination('quarters', 25),
Denomination('dimes', 10),
Denomination('nickels', 5),
Denomination('pennies', 1),
)
def pay(drink: dict[str, Any]) -> float:
"""Checks for payment and its viability and returns how much you paid"""
cost: float = drink['cost']
tender_cents = 0
for coin in DENOMINATIONS:
while True:
try:
tender_cents += coin.input_tender()
break
except ValueError:
pass
tender = tender_cents*0.01
if tender < cost:
print(f'Not enough payment! Refunding {currency(tender)}...')
return 0
if tender > cost:
change = tender - cost
print(f'Returning change of {currency(change)}...')
print(f'Payment Successful! Ordering {drink["name"]}...')
return cost
def prepare(drink: dict[str, Any]) -> bool:
"""Calculates and removes resources from coffee machine if drink resources don't exceed it."""
new_resources = dict(resources)
for resource_name, needed in drink['ingredients'].items():
new = resources[resource_name] - needed
if new < 0:
print(f'Not enough {resource_name}.')
return False
new_resources[resource_name] = new
resources.update(new_resources)
return True
def purchase(drink: dict[str, Any]) -> bool:
"""End-to-end purchase of one drink"""
payment = pay(drink)
if payment > 0:
if prepare(drink):
print(f'Enjoy your {drink["name"]}!')
resources['money'] += payment
else:
print(f'Refunding {currency(payment)}...')
return True
def make_menu_description(
drinks: list[dict[str, Any]], commands: dict[str, Callable],
) -> Iterator[str]:
"""Iterator of strings (one line each) representing the menu"""
yield 'Coffee:'
for drink in drinks:
yield (
f'\t⚪{drink["name"].title()}'
f'[{drink["name"][0].upper()}]: '
f'{currency(drink["cost"])}'
)
yield ''
yield 'Commands:'
for name in commands.keys():
yield (
f'\t⚪{name.title()}'
f'[{name[0].upper()}]'
)
yield ''
def make_menu_choices(
drinks: list[dict], commands: dict[str, Callable],
) -> Iterator[tuple[
str, # menu key
Callable[[], bool] # menu action callable
]]:
"""Make the menu choice key-value iterator; all values are no-argument callables that return
True if the program should continue"""
for drink in drinks:
bound_purchase = partial(purchase, drink=drink)
yield drink['name'][0].upper(), bound_purchase
for name, command in commands.items():
yield name[0].upper(), command
def report() -> bool:
for item, amount in resources.items():
if item == 'money':
desc = currency(amount)
else:
desc = amount
print(f'{item.title()}: {desc}')
return True
def off() -> bool:
# signal main loop to quit
return False
def main() -> None:
setlocale(LC_ALL, '')
with open('resources.json') as f:
drinks = json.load(f)
commands = {'report': report, 'off': off}
menu_desc = '\n'.join(make_menu_description(drinks, commands))
choices = dict(make_menu_choices(drinks, commands))
while True:
print(menu_desc)
choice_str = input('What would you like? ')
perform_action = choices.get(choice_str[:1].upper())
if perform_action is not None and not perform_action():
break
print()
if __name__ == '__main__':
main()https://codereview.stackexchange.com/questions/285277
复制相似问题