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?
答案 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