开发者

How to programmatically add bindings to the current class scope in Python?

Though the question is very specific, I'd also really appreciate general advice and other approaches that would make my question moot. I'm building a collection of AI programs, and many of the functions and classes need to deal with a lot of different states and actions that cause transitions between states, so I need a way to represent states and actions. Please note that I'm not building a simple state machine, but rather a number of different programs (agents) that all take states and return actions as a way of interacting with an environment.

I could use strings, but that's messy if a particular algorithm needs to associate additional information with a state or action, and comparing strings over and over again in long-running programs is wasted overhead. The same sorts of problems arise with other kinds of constants. So my initial idea is to use n开发者_开发百科ested classes, like so:

class DerivedAgent(Agent):
    class StateA(State): pass
    class StateB(State): pass
    ...
    def do_something(state):
        if state is self.StateA:
           ...

This works fairly well, but if there are a number of states and actions, it can take up a lot of space to declare them all, and all of the pass statements are annoying. I'd like to be able to do something like...

class DerivedAgent(Agent):
    states("StateA", "StateB", "StateC", ...)

But I don't see a way to have the states method add the newly-created types to the DerivedAgent class. I think I might be able to do it with the inspect module, but that feels like it's going too far for a small convenience. Is using types like this a bad idea? Is there a much more elegant approach? Code outside of the agent classes will need to be able to access the states and actions, and putting states into the module namespace isn't a good option because a given module might have several agents in it.


You could use meta classes so that you would end up with code like:

class DerivedAgent(Agent):
    __states__ = ['StateA', 'StateB', ...]

for example:

class AgentMeta(type):
    def __new__(meta, classname, bases, classdict):
        for clsname in classdict['__states__']:
            classdict[clsname] = type(clsname, (State,), {})
        return type.__new__(meta, classname, bases, classdict))

then, just rewrite your Agent class so that it has the line

#python3.x
class Agent(Base1, Base2, ..., BaseN, metaclass=AgentMeta):
    #everything else unchanged

# 2.2 <= python <= 2.7 
class Agent(Base1, Base2, ..., BaseN):
    __metaclass__ = AgentMeta
    #everything else unchanged

If you don't want to change the Agent class, you can just include the approriate declaration of metaclass in each subclass of it that you create.


Explicit state machines are boring, you can have implicit state machines in coroutines. But that is probably too much right now.

Anyways class StateA(State): pass is exactly the same as StateA = type("StateA", (State,), {}). Saves you typing the pass ;-)


If you want a state machine, build one. A state machine is data-driven which means that the states and transitions are encoded as data, not class hierarchy.

In Python, dictionaries are the mechanism for ad hoc polymorphic dispatch:

def do_foo(**kwargs):
    pass

def do_bar(**kwargs):
    pass

dispatch = {
    # state : { (transition, next_state) ... }
    0: {'a' : (do_foo, 1)},
    1: {'a' : (do_bar, 0)},
    1: {'b' : (do_bar, None)}, # None -> accept
}

def state_machine(state, input):
    """does the action corresponding to state on input and returns new state"""
    current = dispatch[state]
    if input in current:
        functor, next = current[input]
        functor(lexeme=input)
        return next

state = 0
for c in 'aaab':
    state = state_machine(state, c)
    if state is None:
        print 'accepted'
        break
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜