Python脚本策略:与excec相比,运行脚本两次



我正在编写一个脚本,它接收两个数据列表并查找差异,以便将数据更新到数据库中
这些列表是非同质的:一个是数据库对象列表,另一个是字典列表
出于多种原因,此工具提供了在应用更改之前预览差异列表的机会
分析预览后,如果主持人接受更新,则将应用更改。

这意味着包含大量循环和条件测试的脚本将生成一个预览列表(提供给输出页面)和一个更改列表


在开发的第一阶段,我编写了一个单独的脚本,它将以两种模式运行:"预览"one_answers"更新"
该脚本执行了所有循环和条件检查,如果在某个时刻发现更改,如果在"预览"模式下运行,它将在输出字符串中添加消息,或者如果在"更新"模式下执行命令。

然后我开始认为,在循环和条件检查之间只传递一次可能会更好,只要发现更改,就向输出字符串中添加预览消息,向命令列表中添加命令。

然后,在将预览页面提供给主持人后,如果更改被接受,则运行第二个脚本,该脚本简单如下:

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

问:可以切换到脚本的第二个版本吗?哪些注意事项/错误/奇怪的行为会涉及?


我一直在语言不可知标签下问几乎相同的问题,
因为我对这个问题的算法观点比对它的实现更感兴趣。

但更多地从实现的角度来看,使用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))
    ...

您更喜欢第一个版本的原因之一是能够仅在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...)

这是可能的,因为Python中有一流的函数。这样,您唯一需要维护的东西就可以非常干净地分开,并且不必相互关心

  • diff脚本
  • preview模式下如何处理命令
  • update模式下如何处理命令

没有重复版本的函数,diff函数内部的长期分支可能不会很昂贵,易于维护。

如果能够在不经过preview的情况下直接在update模式下运行并不重要,那么最佳性能选项实际上是在preview模式下构建命令列表,然后在update模式下运行。它与上述解决方案一样易于维护。然而,即使你选择了这个选项,也要考虑使用上面的模式实现它有多容易:

def buildCommandList(other parameters...):
    commandList = []
    generate_diff(commandList.append, other parameters...)
    return commandList

最新更新