修复您的 in_listed[0] == 'add' 测试
如果要将输入字符串分割成单词列表,可以使用strip and split:
in_listed = input('item list: ').strip().split()
然后您可以安全地测试in_listed[0] 和"add"、"rm"、"quit" 之间的相等性。
读取-评估-打印循环
您正在实施的称为“shell”或"Read-eval-print loop"。我建议简化循环的核心,使其读取输入、识别命令并调用适当的命令;每个命令的单独代码可以分离并放入函数中,而不是在循环内巨大的if/elif/else 森林中。
这是简化后循环的样子:
env = { 'todo_list': [], 'prompt': '$ ', 'didntQuit': True }
command_dict = {'add': cmd_add, 'rm': cmd_rm, 'print': cmd_print_todolist, 'quit': cmd_quit}
while env['didntQuit']:
argv = input(env['prompt']).strip().split() # read
command = command_dict.get(argv[0], cmd_notfound) # identify command
ret = command(argv) # execute command
print_return_value(ret, argv) # error message if command failed
我们有一个字典command_dict,其中包含所有命令的名称。在 while 循环体中,我们读取输入;将其拆分为单词列表;在命令字典中查找输入的第一个单词;然后我们评估在字典中找到的命令(或者cmd_notfound,如果我们没有找到具有该名称的命令)。
然后我们可以在while循环外定义函数cmd_add、cmd_rm、cmd_print_todolist和cmd_quit。
请注意,我将所有全局变量都移到了一个名为 env(用于“环境”)的字典中,因为我对全局变量持谨慎态度,我更喜欢将它们全部放在一个地方。
完整代码
这里是完整的代码:
env = {
'todo_list': [],
'prompt': '$ ',
'replname': 'repl-todolist',
'didntQuit': True
}
def cmd_add(argv):
if len(argv) > 2:
date = argv[1]
item = ' '.join(argv[2:])
elif len(argv) == 2:
date = ''
item = argv[1]
else:
return -1
env['todo_list'].append((date, item))
return 0
def cmd_rm(argv):
if len(argv) < 2:
print('rm: please specify the index of the item you want to remove')
return -1
try:
i = int(argv[1])
env['todo_list'].pop(i)
return 0
except ValueError:
print('rm: failed to interpret "{}" as a list index'.format(argv[1]))
return -1
except IndexError:
print('rm: index out of range')
return -1
def cmd_print_todolist(argv):
print('TODO:')
for i, (date, item) in enumerate(env['todo_list']):
print(' ({}) {} {}'.format(i, date, item))
return 0
def cmd_quit(argv):
env['didntQuit'] = False
return 0
def cmd_notfound(argv):
print('{}: {}: command not found'.format(env['replname'], argv[0]))
return -1
def print_return_value(ret, argv):
if ret != 0:
print('{}: command {} failed with return value {}'.format(env['replname'], argv[0], ret))
command_dict = {'add': cmd_add, 'rm': cmd_rm, 'print': cmd_print_todolist, 'quit': cmd_quit}
while env['didntQuit']:
argv = input(env['prompt']).strip().split() # read
ret = command_dict.get(argv[0], cmd_notfound)(argv) # eval
print_return_value(ret, argv) # error message
文件 I/O
使用这种结构的代码,添加新命令很简单。只需编写一个新函数,并将其添加到命令字典中!
我建议你阅读 python 中文件 I/O 的教程:Official Doc on Reading and Writing Files。
这是一个"save" 命令的示例,它将把待办事项列表写入文件:
def cmd_save(argv):
if len(argv) < 2:
print('save: please specify a filename to save the todo-list to')
return -1
filename = argv[1]
try:
with open(filename, 'w') as outf:
for i, (date, item) in enumerate(env['todo_list']):
outf.write('TODO:\n')
for i, (date, item) in enumerate(env['todo_list']):
outf.write(' ({}) {} {}\n'.format(i, date, item))
return 0
except:
print('save: unable to open file "{}"'.format(argv[1]))
return -1
然后你可以将这个函数添加到命令字典中:
command_dict = {'save': cmd_save, 'add': cmd_add, 'rm': cmd_rm, 'print': cmd_print_todolist, 'quit': cmd_quit}
日期和排序待办事项列表
如果您需要解析、操作或打印日期和时间,a python module called datetime 真的很酷。
我建议修改函数cmd_add,将date = argv[1] 行替换为更复杂的代码:
def cmd_add(argv):
if len(argv) > 2:
date = datetime.datetime.strptime(argv[1],"%m/%d").replace(datetime.datetime.today().year)
item = ' '.join(argv[2:])
elif len(argv) == 2:
date = datetime.datetime.today()
item = argv[1]
else:
return -1
env['todo_list'].append((date, item))
env['todo_list'].sort(key=lambda item: item[0])
return 0
以下是新功能:
- 现在
date 不再是字符串,而是datetime 对象;
- 由于用户没有指定年份,我将今天的年份添加到日期(另请参阅this answer);
- 如果用户没有指定日期,我们会添加带有今天日期的项目;
- 我在函数末尾添加了
.sort()。现在您的待办事项列表按日期排序!
由于日期是一个日期时间对象,我们也想修改cmd_print_todolist,以便以良好的格式打印时间:
def cmd_print_todolist(argv):
print('TODO:')
for i, (date, item) in enumerate(env['todo_list']):
print(' ({}) {} {}'.format(i, date.strftime('%m/%d'), item))
return 0
我使用'%m/%d' 来格式化日期,但您可以更花哨并添加星期几,或者用文字写月份等。有关日期解析的更多信息,请参阅strptime and strftime behaviour 上的文档和日期打印。