我想为您提供有关 CLI 的更详细答案,如何通过将代码拆分为函数来清理代码,如何定义自己的异常,以及如何删除两个主要循环之一。此外,我没有将数字相加,而是选择存储每个输入。例如,输出可能是:
{'Computer': [1, 3], # 2 branches with 1 and 3 books
'Physics': [4], # 1 branch with 4 books
'Chemistry': [2, 3, 2],
'Biology': [2],
'Arts': [2]}
代码分为3部分:
- 例外情况
- 输入
- 主循环
您可以定义自己的异常。您可以阅读例如这个article。此外,我选择将用户输入的可能重试次数限制为 5 次。这样,程序总是有结束的。我用大写字母定义了RETRIES,我通常会认为这是一个导入的全局变量。
#%% Define custom exceptions
RETRIES = 5
class ConsecutiveWrongAnswers(Exception):
"""Exception raised when the users input too many times a wrong answer
to a query.
Attributes:
retries -- Number of retries
"""
def __init__(self, retries):
self.retries = retries
self.message = f'The input was invalid more than {self.retries} times!'
super().__init__(self.message)
class InvalidBranchNumber(Exception):
"""Exception raised for errors in the branch number input.
Attributes:
branch -- invalid branch number.
message -- explanation of the error.
"""
def __init__(self, branch,
message="Branch number input should be a positive integer."):
self.branch = branch
self.message = message
super().__init__(self.message)
def __str__(self):
return f'{self.branch} -> {self.message}'
class InvalidBooksNumber(Exception):
"""Exception raised for errors in the branch number input.
Attributes:
books -- invalid books number.
message -- explanation of the error.
"""
def __init__(self, branch,
message="Books number input should be a positive integer."):
self.books = books
self.message = message
super().__init__(self.message)
def __str__(self):
return f'{self.books} -> {self.message}'
然后,我定义了输入函数,它允许用户尝试输入最多RETRIES 次的有效输入。在返回之前检查输入。
#%% Input functions
def input_branch_number(category, retries=RETRIES):
def check_branch(branch):
if branch <= 0:
raise InvalidBranchnNumber(branch)
attempt = 0
while True:
attempt += 1
if attempt > retries:
raise ConsecutiveWrongAnswers(retries)
try:
branch = int(input(f'[IN] Branch number for category {category}: '))
check_branch(branch)
break
except (InvalidBranchNumber, ValueError, TypeError):
print (f"Invalid ID: branch number should be a positive integer.)")
except:
raise
return branch
def input_books_number(category, branch_id, retries=RETRIES):
def check_books(books):
if books <= 0:
raise InvalidBooksNumber(books)
attempt = 0
while True:
attempt += 1
if attempt > retries:
raise ConsecutiveWrongAnswers(retries)
try:
books = int(input(f'[IN] Books number for (category, branch n°) - ({category}, {branch_id}): '))
check_books(books)
break
except (InvalidBooksNumber, ValueError, TypeError):
print (f"Invalid ID: branch number should be a positive integer.)")
except:
raise
return books
最后,主循环现在更易于阅读:
#%% Main loop
# Initialize the data structure which will contain the number of books per category and per branch.
books_logs = dict()
for category in ["Computer", "Physics", "Chemistry", "Biology", "Arts"]:
# Input the number of branch per category
branch = input_branch_number(category)
# For each category, initialize an empty list. The list will be filled by the number of books per branch
books_logs[category] = list()
for branch_id in range(branch):
# Input the books for each (category, branch_id)
books = input_books_number(category, branch_id)
books_logs[category].append(books)
您现在可以使用 books_logs 字典来对每个分支、每个类别进行求和,或者得到总数。
注意:对于字符串格式,我建议您使用提高可读性的 python 格式标志,而不是像您那样对 str 对象求和:
f'This is a formatting string with {n_characters} characters and {n_words}.'
{} 之间的元素可以是任何东西,它将由 python 评估/执行。你可以有公式,例如
k = 2
f'I want to format with leading zeros integer {k} to get {str(k).zfill(3)}'
最后,我选择了一个非常简单的数据结构,但是你可以将字典替换为另一个。特别是,正如 Ruthger Righart 所建议的,您可以使用 pandas 数据框。