我正在编写一个脚本,它接收两个数据列表并查找差异,以便将数据更新到数据库中。
这些列表是非同类的:一个是数据库对象列表,另一个是字典列表
出于多种原因,此工具提供了在应用更改之前预览差异列表的机会
在分析预览后,如果主持人接受更新,则将应用更改。
这意味着包含大量循环和条件测试的脚本将生成预览列表(提供给输出页面)和更改列表。
在开发的第一阶段,我写了一个单独的脚本,它将以两种模式运行:'预览'和'更新'
此脚本执行所有循环和条件检查,如果在某些时候发现了更改,则在“预览”模式下运行时会向输出字符串添加消息,或者如果在“更新”模式下运行则执行命令。
然后我开始认为,只要在循环和条件检查之间传递一次,将预览消息添加到输出字符串并将命令发送到命令列表,只要发现更改,就可能更好。
然后,在向主持人提供预览页面后,如果接受了更改,请运行第二个脚本,这将非常简单:
def apply_changes(command_list, ...):
for c in command_list:
try:
exec(c)
except Exception, err:
logger.error('Something went wrong while executing command %s: %s', c, err)
raise Exception, err
问:可以切换到脚本的第二个版本吗?哪些警告/错误/陌生行为会涉及?
我一直在问almost the same question under the language-agnostic tag,
因为我对问题的算法观点比对它的实现更感兴趣。
但是更多地关注实现的观点,似乎在Python中,这个脚本的第二个版本比第一个版本更好,并且比第一个版本更容易维护。
有什么理由我更喜欢第一个版本吗?
编辑:添加一些代码,以便进一步澄清问题。
第一个版本代码的摘录将类似于此函数(在更容易的情况下使用,其中比较的值都是字符串),如果满足某些特定条件,则由嵌套for循环内的其他函数调用:
def update_text_field(object, old_value, new_value, field_name, mode):
....
if old_value != new_value:
if mode is 'preview': output += print_old_new_values(old_value, new_value, field_name)
if mode is 'update':
if hasattr(object, field_name):
setattr(object, field_name, new_value)
object.save()
else:
...
....
在第二个版本中,这段摘录会变成这样的东西:
def update_text_field(object, old_value, new_value, field_name, mode):
....
if old_value != new_value:
output_list.append(print_old_new_values(old_value, new_value, field_name))
command_list.append(generate_command(command, old_value, new_value, field_name))
...
答案 0 :(得分:1)
您更喜欢第一个版本的一个原因是能够仅以update
模式运行脚本(跳过强制在第二个脚本preview
步骤。)
以下是关于如何使脚本更易于维护的建议:在脚本运行时抽象出模式。这可以这样做:
def generate_diff(f, other parameters...):
# ... Do computations ...
# Whenever command "c" needs to be previewed/executed, do:
f(c)
# ... More computations...
然后你可以定义两种模式(这只是一个简单的例子):
def _previewMode(command):
print command
def _updateMode(command):
exec(command)
并可选择定义便利包装器:
def preview(other parameters...):
return generate_diff(_previewMode, other parameters...)
def update(other parameters...):
return generate_diff(_updateMode, other parameters...)
甚至可以轻松定义新模式:
def _interactiveMode(command):
if raw_input('Execute command ' + command + '?').lower() in ('yes', 'y'):
print 'Command returned:', exec(command)
def interactive(other parameters...):
return generate_diff(_interactiveMode, other parameters...)
由于first-class functions in Python,这是可能的。通过这种方式,您需要维护的唯一内容非常清晰,不必彼此关心:
preview
模式下如何处理命令update
模式下如何处理命令没有重复版本的函数,在diff函数中没有可能很长的分支,很容易维护。
如果能够直接在update
模式下运行而不经过preview
并不重要,那么最佳性能选项确实是在preview
模式下构建命令列表,然后以update
模式运行它。它与上述解决方案一样易于维护。但是,即使你选择了这个选项,也要考虑使用上述模式实现它是多么容易:
def buildCommandList(other parameters...):
commandList = []
generate_diff(commandList.append, other parameters...)
return commandList