2012-03-02 118 views
1

我正在編寫一個腳本,它接收兩個數據列表並查找差異,以便將數據更新到數據庫中。
這些列表是非同類的:一個是數據庫對象列表,另一個是字典列表。
由於很多原因,此工具提供了在應用更改之前預覽差異列表的機會。
分析預覽後,如果主持人接受更新,則會應用更改。

這意味着包含大量循環和條件測試的腳本將生成預覽列表(投放到輸出​​頁面)和更改列表。
Python腳本編制策略:兩次運行腳本vs excec


在開發的第一個階段,我編寫了一個腳本,它將以兩種模式運行:'預覽'和'更新'。
該腳本執行所有循環和條件檢查,並且如果在某個點發現更改,則會在「預覽」模式下運行時向輸出字符串添加消息,或者在「更新」模式下運行時執行命令。

然後我就開始想,也許這將是更好的循環和條件檢查的間隔僅通過一次,加入預覽郵件發送到輸出字符串命令的命令列表,每當一個變化中找到。

然後,服務預覽頁面主持人後,如果接受更改,運行第二個腳本,這會是這樣簡單:

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
因爲我比它的實現更感興趣的是考慮問題的algorithmical點。

但是更多地關注實現的觀點,似乎與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

如果你可以掛鉤的變化,爲什麼不來構建的變化,而不是蟒蛇命令列表的隊列? – Odomontois 2012-03-02 00:25:05

+0

'command_list'從哪裏來?這個'exec'看起來像是一個潛在的安全漏洞,可以通過某種你可以定義的應用函數來輕鬆避免。 – wim 2012-03-02 00:26:06

+0

@wim:從腳本本身生成(將清晰地顯示一些代碼) – dolma33 2012-03-02 00:37:27

回答

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 
+1

+1,因爲你理解我的疑惑是100%,對於一流的功能是+1000) – dolma33 2012-03-02 00:41:41