2016-04-25 103 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

非常感谢您的支持。我真的不得不学习这个'描述符'魔术 –

相关问题