
本文探讨了如何通过函数化和数据结构优化python程序中重复的条件判断,以一个命令行计算器为例。文章详细介绍了如何设计一个通用的用户输入函数,结合lambda表达式进行输入验证,并利用字典存储操作函数,从而有效重构复杂的if-elif链,提升代码的模块化、可读性和维护性,并简化程序流程控制。
在交互式程序开发中,我们经常需要从用户那里获取输入,并根据输入进行验证和流程控制。原始的Python计算器代码中存在以下几个明显的痛点:
这些问题共同导致了代码的可读性和可维护性下降。接下来,我们将通过函数化和数据结构化的方式,对代码进行全面优化。
为了解决重复的输入获取、验证和流程控制问题,我们可以设计一个高度通用的get_user_input函数。这个函数将负责所有用户输入相关的交互逻辑,包括提示、特殊字符处理、输入验证和错误消息显示。
def get_user_input(prompt, validator, error_msg):
"""
获取用户输入,并进行验证。
Args:
prompt (str): 显示给用户的提示信息。
validator (callable): 一个可调用对象(函数或lambda表达式),
用于验证用户输入。如果输入有效,应返回非False值;
如果无效,应返回False或抛出ValueError。
error_msg (str): 当输入验证失败时,显示给用户的错误信息。
Returns:
str: 经过验证的有效用户输入,或特殊控制字符('$')。
"""
while True:
user_input = input(prompt)
print(user_input) # 打印用户输入,与原代码行为一致
# 处理特殊控制字符
if user_input.endswith('$'):
return "$" # 返回'$'表示需要重置程序状态
if user_input.endswith('#'):
exit() # 以'#'结尾直接退出程序
# 尝试使用validator验证输入
try:
# 如果validator返回非False值,则认为输入有效
if validator(user_input) is not False:
return user_input
except ValueError:
# validator抛出ValueError(如float()转换失败)时捕获
pass
# 验证失败,打印错误信息并继续循环
print(error_msg)
get_user_input函数详解:
立即学习“Python免费学习笔记(深入)”;
通过将验证逻辑抽象到validator参数中,get_user_input函数变得高度灵活,可以适应各种输入验证场景。
现在,我们可以利用这个通用的get_user_input函数来重构计算器的输入部分。
对于操作符选择,我们需要确保用户输入的是有效的操作符集合中的一个。我们可以使用lambda表达式作为validator来简洁地实现这一点。
# ... (主循环和菜单打印) ...
choice = get_user_input("Enter choice (+, -, *, /, ^, %, #, $): ",
lambda x: x in ("+", "-", "*", "/", "^", "%"),
"Unrecognised operation")
if choice == '$':
continue # 如果用户输入'$',则跳过当前循环,重新开始主循环这里,lambda x: x in ("+", "-", "*", "/", "^", "%")作为一个匿名函数,检查用户输入x是否在允许的操作符元组中。
对于第一个和第二个操作数,它们都是浮点数。float()函数本身就可以作为验证器:如果输入是有效的数字字符串,float()会成功转换;否则,它会抛出ValueError。我们可以利用一个for循环来优雅地处理两个数值的输入。
operands = []
for prompt in ("First number: ", "Second number: "):
number_str = get_user_input(prompt, float, "unidentified operand")
if number_str == '$':
break # 如果用户输入'$',则跳出当前for循环,准备重置主循环
operands.append(float(number_str)) # 验证通过后,将字符串转换为浮点数并存储
else: # 只有当for循环没有被break时,才会执行else块
# ... 进行计算 ...这里的for-else结构非常巧妙:else块只在for循环正常完成(即没有遇到break语句)时执行。这意味着只有当两个操作数都被成功获取且没有触发重置时,才会进入计算阶段。
原始代码使用一系列if-elif语句来执行不同的算术运算。这种模式可以通过字典来优化,实现所谓的“策略模式”。我们将操作符作为字典的键,而对应的算术函数作为值。
import operator # 可以选择导入operator模块以使用内置函数
funcs = {
'+': lambda a, b: a + b,
'-': lambda a, b: a - b,
'*': lambda a, b: a * b,
'/': lambda a, b: a / b,
'^': lambda a, b: a ** b,
'%': lambda a, b: a % b,
# 如果使用operator模块,可以这样定义:
# '+': operator.add,
# '-': operator.sub,
# '*': operator.mul,
# '/': operator.truediv,
# '^': operator.pow,
# '%': operator.mod,
}现在,执行计算就变得非常简洁:
try:
# 从字典中获取对应的函数,并使用*operands解包参数
result = funcs[choice](*operands)
except ZeroDivisionError:
result = "Can't divide by zero" # 处理除零错误
print(result)通过这种方式,我们完全消除了冗长的if-elif链,使得代码更易于扩展。如果需要添加新的运算,只需在funcs字典中添加新的键值对即可。
将上述所有优化整合到主程序循环中,我们可以得到一个更加简洁、模块化且易于维护的计算器程序。
def get_user_input(prompt, validator, error_msg):
while True:
user_input = input(prompt)
print(user_input)
if user_input.endswith('$'):
return "$"
if user_input.endswith('#'):
exit()
try:
if validator(user_input) is not False:
return user_input
except ValueError:
pass
print(error_msg)
# 定义操作函数字典
funcs = {
'+': lambda a, b: a + b,
'-': lambda a, b: a - b,
'*': lambda a, b: a * b,
'/': lambda a, b: a / b,
'^': lambda a, b: a ** b,
'%': lambda a, b: a % b,
}
while True:
print("Select operation.")
print("1.Add : + ")
print("2.Subtract : - ")
print("3.Multiply : * ")
print("4.Divide : / ")
print("5.Power : ^ ")
print("6.Remainder: % ")
print("7.Terminate: # ")
print("8.Reset : $ ")
# 获取操作符
choice = get_user_input("Enter choice (+, -, *, /, ^, %, #, $): ",
lambda x: x in ("+", "-", "*", "/", "^", "%"),
"Unrecognised operation")
if choice == '$':
continue # 重置主循环
# 获取两个操作数
operands = []
for prompt in ("First number: ", "Second number: "):
number_str = get_user_input(prompt, float, "unidentified operand")
if number_str == '$':
break # 跳出当前for循环,准备重置主循环
operands.append(float(number_str))
else: # 只有当两个操作数都成功获取时,才执行计算
try:
result = funcs[choice](*operands)
except ZeroDivisionError:
result = "Can't divide by zero"
print(result)
# 询问是否进行另一次计算
proceed_choice = get_user_input("Want to perform another calculation (Y/N) ",
lambda x: x.upper() in ("Y", "N"),
"Unrecognised answer").upper()
if proceed_choice == 'N':
break # 退出主循环
elif proceed_choice == '$':
continue # 重置主循环 (get_user_input会返回'$',但这里我们已经将其转换为大写,需要额外处理或调整get_user_input的返回逻辑)
# 注意:如果get_user_input返回'$',则proceed_choice会是'$',其.upper()仍是'$'。
# 原始get_user_input已处理'$'和'#'并直接返回,这里是针对Y/N的验证。
# 如果希望'$'也能重置,需要将get_user_input的返回值直接用于判断,而不是先upper()
# 改进:get_user_input返回特殊字符,或返回经过验证的规范化输入
# 为了保持get_user_input的通用性,这里假设它已经处理了'$'和'#'的退出/重置逻辑。
# 实际上,get_user_input返回'$'或'#'后,外层调用者需要判断并执行相应的continue/exit。
# 在当前设计中,如果get_user_input返回'$',它会被赋给proceed_choice,然后.upper()后仍是'$'。
# 如果想让它重置,需要显式判断。
# 修正逻辑:如果get_user_input直接返回'$',外层应处理。
# 例如:
# user_response = get_user_input("Want to perform another calculation (Y/N) ", ..., ...)
# if user_response == '$':
# continue
# elif user_response.upper() == 'N':
# break
# else: # 'Y'
# continue
pass # 这里的pass表示'Y',继续循环。如果用户输入'$',get_user_input会直接返回'$',
# 并且外层需要检查。当前代码中,get_user_input的validator会先尝试验证Y/N,
# 如果是'$'或'#',get_user_input会直接返回或退出。
# 所以这里的proceed_choice不会是'$'或'#',只会是'Y'或'N'。
注意事项:
通过本次重构,我们显著提升了Python计算器代码的质量:
这种函数化、参数化和数据结构化的思想是编写高质量、可维护代码的关键。在开发交互式命令行工具或任何需要处理多种条件分支的场景时,都应优先考虑采用类似的设计模式,以减少代码的复杂性,提高开发效率。
以上就是Python计算器重构:用函数和字典优化条件判断的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号