2014-10-28 30 views
1

我想弄清楚最傳統的Pythonic方法來進行有限的回調,以便與複雜對象的方法進行交互。我有一個通過通信渠道接收數據的程序,該數據分爲幾個不同的類別。我需要去耦哪個類別的數據是在確定,從數據的進一步處理:相當於Java內部類的代理的Python

class Categorizer(object): 
    '''Determines which category data is in. 
     There may be multiple variants of this class''' 
    def handleData(self, data, callback): 
     if self.somethingReallySpecial(data): 
      callback.onSomethingReallySpecial(data) 
     elif self.somethingSpecial(data): 
      callback.onSomethingSpecial(data) 
     else: 
      callback.onSomethingMundane(data) 
    # ... other methods ... 

class IAmAReallyComplicatedBeast(object): 
    def __init__(self, categorizer, other_stuff): 
     self.categorizer = categorizer 
     # ... 
    # ... lots of other methods ... 
    def something(self, other_stuff): 
     data = self.obtain_data() 
     # this is probably wrong, but here's what I want to do: 
     beast = self 
     class Dispatcher(object): 
      def onSomethingMundane(data): 
       beast.doFoo(data) 
      def onSomethingSpecial(data): 
       beast.doBar(data) 
      def onSomethingReallySpecial(data): 
       beast.doBaz(data) 
     self.categorizer.handleData(data, Dispatcher()) 
    def doFoo(self, data): 
     # for mundane data 
    def doBar(self, data): 
     # for special data 
    def doBaz(self, data): 
     # for really special data 

在Java中我會用一個內部類(喜歡這裏的調度程序)...有一個Python化處理這個方法?


我不想直接把onSomethingXXX方法對我IAmAReallyComplicatedBeast類,有兩個原因:

  • 這意味着我不得不爲是
  • 我不使用這些名稱不希望Categorizer類可以任意訪問IAmAReallyComplicatedBeast對象。也許這來自通常的Java偏執狂思維,但它對我來說似乎是一個很好的設計。
+1

我會用'dispatch dict'。在Python中,字典可以保存函數。 – ch3ka 2014-10-28 16:39:09

+0

我認爲內心階層總是正是你想要的,也許是最好的方式來做到這一點。這些大腿永遠不可能是pythonich,因爲python不是爲了做這種保護而設計的。 – 2014-10-28 17:51:01

回答

2

使調度員的Python的方式是使用字典。請記住,在Python函數中是一流的對象,因此可以是字典中的值。

class IAmAReallyComplicatedBeast(object): 
    def something(self, other_stuff): 
     data = self.obtain_data() 
     dispatcher = { 
      'something_mundane': self.do_foo, 
      'something_special': self.do_bar, 
      'something_really_special': self.do_baz 
     } 
     self.categorizer.handleData(data, dispatcher) 

class Categorizer(object): 
    '''Determines which category data is in. 
     There may be multiple variants of this class''' 
    def handleData(self, data, callback): 
     if self.somethingReallySpecial(data): 
      dispatcher['something_really_special'](data) 

注:我知道你並沒有提出這一點,但內部類是真正的非Python化:內部類漲幅沒有特殊訪問外部類,以及諸如此類的事情,不推薦。

+0

有關性能的問題:我的分類程序調度每秒調用數千次;與屬性查找相比,字典查找的成本是多少? – 2014-10-28 16:52:33

+0

他們*完全相同*。 Dict查找無論如何都是O(1),但更重要的是屬性被實現爲字典。 – 2014-10-28 16:53:39

+0

關於使用'namedtuple'而不是'dict'的想法?我知道這不是必要+它是不可變的,但似乎這可能是一個更好的方法(如果沒有提供預期的方法,就會快速失敗)。 – 2014-10-28 16:56:57

1

可以使IAmAReallyComplicatedBeast也是一個Dispatcher通過別名必要的方法:

class IAmAReallyComplicatedBeast(object): 
    # ... snip ... 

    # In something we can just pass ourself because we are a dispatcher 
    self.categorizer.handleData(data, self) 
    # ... snip ... 
    def doFoo(self, data): 
     # Do mundane things here 

    onSomethingMundane = doFoo 

    # ... etc. ... 

或者,您可以創建一個包裝方法的類簡單地創建實例它,而不是每次創建一個新類:

class Dispatcher(object): 
    __slots__ = ('onSomethingMundane', 
       'onSomethingSpecial', 
       'onSomethingVerySpecial') 
    def __init__(self, mundane, special, very_special): 
     self.onSomethingMundane = mundane 
     self.onSomethingSpecial = special 
     self.onSomethingReallySpecial = very_special 

然後您的something方法將是一個更清楚一點:

def something(self, other_stuff): 
     data = self.obtain_data() 
     dispatcher = Dispatcher(self.doFoo, self.doBar, self.doBaz) 
     self.categorizer.handleData(data, dispatcher) 
+0

是的,我可以,但我不想......編輯的問題。 – 2014-10-28 16:46:54

+0

好的,感謝您的更新建議重新:分派器包裝。 – 2014-10-28 17:09:35

1

如果您有興趣分享與其他類調度員,你可以做這樣的事情:

class Dispatcher(object): 
    def __init__(self,f1,f2,f3): 
     self.onSomethingMundane=f1 
     self.onSomethingSpecial=f2 
     self.onSomethingReallySpecial=f3 


class IAmAReallyComplicatedBeast(object): 
    #... 
    def something(self, other_stuff): 
     data = self.obtain_data() 
     # this is probably wrong, but here's what I want to do: 
     beast = self 
     beast_dispatcher = Dispatcher(beast.doFoo,beast.doBar,beast.doBaz) 
     self.categorizer.handleData(data, beast_dispatcher) 
    #... 
1

由於@ ch3ka已經表明,字典將pythonic選擇在這裏恕我直言。 事情可能喜歡這則:

class Categorizer(object): 
    '''Determines which category data is in. 
     There may be multiple variants of this class''' 
    def handleData(self, data, callback_mapping): 
     # get the category 
     category = self.categorize(data) 
     # invoke the corresponding handler 
     callback_mapping[category](data) 


class IAmAReallyComplicatedBeast(object): 
    def __init__(self, categorizer, other_stuff): 
     self.categorizer = categorizer 
     # ... 
    # ... lots of other methods ... 
    def something(self, other_stuff): 
     data = self.obtain_data() 
     self.categorizer.handleData(data, 
            dict(mundane=self.doFoo, 
             special=self.doBar, 
             really_special=self.doBaz) 
    def doFoo(self, data): 
     # for mundane data 
    def doBar(self, data): 
     # for special data 
    def doBaz(self, data): 
     # for really special data 

經常使用的另一種模式是創建方法動態調用的名稱。 例如在python的內置BaseHTTPServerdo_XXX被調用,其中XXX是請求的佔位符的HTTP方法:

mname = 'do_' + self.command 
    if not hasattr(self, mname): 
     self.send_error(501, "Unsupported method (%r)" % self.command) 
     return 
    method = getattr(self, mname) 
    method() 

參見:https://hg.python.org/cpython/file/2.7/Lib/BaseHTTPServer.py#l323 因此你可以如爲您的方法命名爲doSpecialdoReallySpecialdoMundane,並從分類程序中調用它們。