2016-04-25 107 views
3

我在Django上構建SPA,我有一個巨大的函數,有很多if語句用於檢查我的對象字段的狀態名稱。像這樣:基於值而不是類型的Singledispatch

if self.state == 'new': 
    do some logic 
if self.state == 'archive': 
    do some logic 

等等。我現在在閱讀漂亮的書籍「Fluent python」,並且我提到了@singledispatch裝飾器,它看起來很棒,但它只能用於str,int等不同類型的參數的覆蓋函數。
問題是,如果存在python或Django的方式來分開像我的巨大功能邏輯,像singledispatch這樣的override函數嗎?

回答

3

有,雖然你必須寫它。一種可能性是創建一個descriptor,做基於instance.state或任何選擇的state_attr調度:

class StateDispatcher(object): 

    def __init__(self, state_attr='state'): 
     self.registry = {} 
     self._state_attr = state_attr 

    def __get__(self, instance, owner): 
     if instance is None: 
      return self 

     method = self.registry[getattr(instance, self._state_attr)] 
     return method.__get__(instance, owner) 

    def register(self, state): 
     def decorator(method): 
      self.registry[state] = method 
      return method 

     return decorator 

https://docs.python.org/3/howto/descriptor.html#functions-and-methods

爲了支持方法調用,函數包括用於屬性訪問期間結合方法__get__()方法。這意味着所有函數都是非數據描述符,它們返回綁定或未綁定的方法,具體取決於它們是從對象還是類調用。

在有狀態類,你可以創建一個調度員和註冊方法:

class StateMachine(object): 

    dispatcher = StateDispatcher() 
    state = None 

    @dispatcher.register('test') 
    def test(self): 
     print('Hello, World!', self.state) 

    @dispatcher.register('working') 
    def do_work(self): 
     print('Working hard, or hardly working?', self.state) 

讓我們來看看它在行動:

>>> sm = StateMachine() 
>>> sm.state = 'test' 
>>> sm.dispatcher() 
Hello, World! test 
>>> sm.state = 'working' 
>>> sm.dispatcher() 
Working hard, or hardly working? working 
>>> sm.state = None 
>>> sm.dispatcher() 
Traceback (most recent call last): 
    ... 
    File "dispatcher.py", line 11, in __get__ 
    method = self.registry[getattr(instance, self._state_attr)] 
KeyError: None 

請注意,這是調度的一個非常邪惡的方法基於狀態,因爲對於未來的代碼讀者來說,整個機制將很難遵循。

調度文本狀態的另一種方法是在方法名稱中對狀態進行編碼,並根據調度函數中的選擇正確的方法。很多python類都使用這種模式(例如ast.NodeVisitor):

class StateMachine(object): 

    def dispatch(self, *args, **kwgs): 
     getattr(self, 'do_{}'.format(self.state))(*args, **kwgs) 

    def do_new(self): 
     print('new') 

    def do_archive(self): 
     print('archive') 


sm = StateMachine() 
sm.state = 'new' 
sm.dispatch() 
sm.state = 'archive' 
sm.dispatch() 
+1

非常感謝您的支持。我真的不得不學習這個'描述符'魔術 –

相關問題