Alternative to multi-line lambdas: Assign-anywhere def statements

Tim Chase python.list at
Sat Jan 24 13:23:38 CET 2015

On 2015-01-24 17:28, Chris Angelico wrote:
> but this is hardly generic. There's no convenient way to give an
> argument to a decorator that says "please assign this here", short
> of using some stupid eval hack... is there?
> (Incidentally, for a non-generic dispatch table, a callable dict
> subclass could be pretty clean:
> class DispatchTable(dict):
>     """Self-populating dictionary - use this as a function decorator
> to stuff functions into it."""
>     def __call__(self, func):
>         self[func.__name__] = func
>         return func
> cmd = DispatchTable()
> @cmd
> def foo(*args):
>     print("You asked to foo.")

As best I can tell, your question is "is there a better way to do
dispatch tables without duplicating the storage location while
remaining sufficiently generic?"

The closest I've seen is something like cmd.Cmd does where you have a
class and dispatch methods on it with a certain naming convention
(this prevents odd access to dunder methods).  Akin to your first
example [reordered from your original post]

> cmd = {} # Command dispatch table
> def cmd["foo"](*args):
>     print("You asked to foo.")
> def cmd["bar"](*args):
>     print("There's no wine in the bar.")
> def cmd["asdf"](*args):
>     print("Asdf! Asdf!")

It usually looks something like

  class Dispatcher(object):
    def __getitem__(self, name):
      return getattr(self, "do_" + name)

  class MyExample(Dispatcher):
    def do_hello(self, who):
      print("Hello, %s" % who)
    def do_goodbye(self, who, feeling):
      print("Goodbye, %s. I'm %s to see you go" % (
        who, feeling))

  mydispatch = MyExample()
  mydispatch["goodbye"]("cruel world", "sorry")
  mydispatch["does not exist"]("give me an AttributeError")

The root Dispatcher object can grow helpful methods like

  class Dispatcher(object):
    # ...
    def get_help(self, name):
      return getattr(self, name, "No help for %r" % name)
    def __iter__(self):
      for name in dir(self):
        if name.startswith("do_") and callable(getattr(self, "do_" + name)):
          yield name
  # ...
    def do_hello(self, who):
      "Say hello to a person"
      # ...

  # print out all known methods and their help
  for method in mydispatch:
    print("%s: %s" % (method, mydispatch.get_help(method_name)))


More information about the Python-list mailing list