Singledispatch based on value instead of type

时间:2016-04-25 09:06:39

标签: python django

I building SPA on Django and I have one huge function with many if statement for checking state name of my object field. Like this:

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

and so on. I reading nice book "Fluent python" now, and I mention about @singledispatch decorator, it looks so great, but it can overide function only with diferent type of parametres like str, int, etc.
Question is, if there in python or Django way to separate logic like in my huge function with overided function like singledispatch do?

2 个答案:

答案 0 :(得分:5)

虽然你必须写它,但有。一种可能性是创建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 :(得分:2)

另一种方式,如果你想使用单分派,就是为状态定义状态类。您可以使它们“尽可能像字符串一样”。例如。类似:

from functools import singledispatch

class State:
    @classmethod
    def make_state(cls, state_name: str) -> 'State':
        state = type(state_name, (State,), {})
        setattr(cls, state_name, state)
        return state()

    def __str__(self):
        return self.__class__.__name__


state_a = State.make_state("a")
state_b = State.make_state("b")

@singledispatch
def foo(s: State) -> str:
    raise NotImplementedError("Used dispatched")

@foo.register
def foo_a(s: State.a):
    return 'a'

@foo.register
def foo_b(s: State.b):
    return 'b'

print(foo(state_a))
print(foo(state_b))
# prints:
# a
# b