2016-02-19 85 views
2

我有一個用於數據分析的類。通常我需要創建一個類的幾個實例,然後在每個實例上運行一系列的方法。我需要運行的方法因實例而異。Runner方法作爲方法鏈接的替代方法嗎?

今天我的代碼(使用類)通常看起來是這樣的:

object_alpha = MyClass() 
object_alpha.method1() 
object_alpha.method2() 
object_alpha.method3(arg1) 
object_alpha.method4() 

object_bravo = MyClass() 
object_bravo.method1() 
object_bravo.method3(arg1) 
object_bravo.method4() 
object_bravo.method5() 

我知道上面的例子不是方法鏈的一個例子。方法鏈現在不可能,因爲方法不返回類的一個對象。

這種格式有點冗長乏味,尤其是對於很長的描述性變量名稱。我主要的抱怨是我不覺得它很可讀。

我想過改變我的類從每個方法調用返回一個新的對象,以便我可以做方法鏈接。但是這些方法的副作用不會改變這個類 - 它們通過一個API來改變數據庫,所以返回一個新的對象感覺很奇怪。

我的想法是創建一個將方法名稱列表作爲字符串的runner類。所以我可以做這樣的事情。

object_alpha = MyClass().runner([ 
    'method1', 
    'method2', 
    'method3(arg1)', 
    'method4' 
]) 

object_bravo = MyClass().runner([ 
    'method1', 
    'method3(arg1)', 
    'method4', 
    'method5' 
]) 

這是一個壞主意;有更好的方法嗎?

+1

1-在您的問題中明確指出您明白您的代碼示例不代表方法鏈接。 2 - ''method3(arg1)''不像其他人,否則你可以在名稱中使用'methodname:getattr(obj,methodname)()'3-是的,這是一個壞主意。爲避免指定對象名稱,您將引入更多級別的間接引用。你想買什麼?提供實際的代碼,否則問題太廣泛(指定一個更好的選擇)。 4-看看[sqlalchemy'如何使用方法鏈接](http://docs.sqlalchemy.org/en/latest/glossary.html#term-method-chaining) – jfs

+1

我沒有看到你從你的亞軍獲得的東西例。編寫它仍然很乏味,但是會增加程序的複雜性。請參閱[內部平臺效果](https://en.wikipedia.org/wiki/Inner-platform_effect)。從這個角度來看,不,它在設計上看起來不是一個好主意。 –

+0

感謝您的評論,我嘗試按照建議清理問題。至於我想獲得什麼,關於可讀性的更多時間和更多。 – Chris

回答

1

基本上,你正試圖在這裏發明一個特定於領域的語言。因爲它的唯一目的是執行一些非常像Python的東西,所以只需不必命名上下文對象,我不認爲這是值得的麻煩。從方法返回對象以允許方法鏈接的模式,即使對象不是操作的邏輯結果,實際上並不是聞所未聞的,所以我願意爲此而努力。

1

你的想法很好,但它可以使用一個主要的改進:不是使用你必須評估的字符串列表,而是使用元組列表或類似的東西來包含方法本身及其參數:

object_bravo = MyClass() 
bravo_runner = [ 
    (object_bravo.method1, (arg1, arg2), {k1: v1, k2, v2}), 
    (object_bravo.method2, (arg3), {}), 
    (object_bravo.method3,(), {k3: v3}), 
    (MyClass.method4, (object_bravo, arg4), {k4: v4}) 
] 

運行,這將是比解析字符串容易得多的方式:

for spec in bravo_runner: 
    spec[0](*spec[1], **spec[2]) 

如果使用namedtuple獲得亞軍的元素,它會更好:

from collections import namedtuple 
RunnerSpec = namedtuple('RunnerSpec', ['method', 'args', 'kwargs']) 
object_bravo = MyClass() 
bravo_runner = [ 
    RunnerSpec(object_bravo.method1, (arg1, arg2), {k1: v1, k2, v2}), 
    RunnerSpec(object_bravo.method2, (arg3), {}), 
    RunnerSpec(object_bravo.method3,(), {k3: v3}), 
    RunnerSpec(MyClass.method4, (object_bravo, arg4), {k4: v4}) 
] 

run方法:

for spec in bravo_runner: 
    spec.method(*spec.args, **spec.kwargs) 

結論

在這一點上,你實際上可能只是寫在類中的方法爲使用self每個序列/方案節省一些打字而不是描述性的對象名稱。這將最終爲您節省最多,因爲您可以預先構建命名的調用序列。無需將它們存儲爲列表。 bravo_object.run_scenario3()bravo_object.runner([big_ass_hard_to_read_list_that_is_basically_a_method_anyway]).run()好。

+0

您可以使用'bravo_runner'中的方法,參數,kwargs而不是'bravo_runner'中的spec。雖然我在第一個代碼示例中沒有看到太多改進,但第二個代碼比問題中的代碼更糟糕。 – jfs

+0

是的。因此得出結論。儘管我有更好的判斷,但我回答了字面問題。好吧。 –