If action comes from outside the program, this can open up unexpected behavior. The dispatch table is closed - there's less chance that user input will invoke something unintended.
In a real world program you would validate input and make sure you get something callable (with either technique). The only additional unexpected behavior is when there could be unexpected handle_xxx attributes. If we take for granted that the programmer defines the dict for the explicit dispatch table, though, we can also assume that the programmer defines the object for the implicit table.
IMO as code gets modified by other developers, the table is easier to keep 'safe' since it is clearer that is what it's for, the sprintf and all the other methods don't tend to be in one place - as methods get added, the reflection issue is oft overlooked.
I feel the version from the original comment is most Pythonic. The other version is close to an eval-function, which seems to be frowned up on in Python.
The second version is slower too. Here's what I use for e.g. a state machine, which follows "don't repeat yourself" a little better than the first example:
# {state_name: state_action}
states = {}
state = ['start']
def reg(func):
states[func.__name__] = func
return func
# define and simultaneously register the states
@reg
def start():
state[0] = 'second'
@reg
def second():
state[0] = 'last'
@reg
def last():
state[0] = None
# run the state machine
while state[0]:
print state[0]
states[state[0]]()