Search code examples
pythontypechecking

design recommendation: control flow based on argument type in Python?


I am writing a function of the form:

def fn(adict, b):
    """`adict` contains key(str): value(list). if `b` is a dict we have to
       call `do_something` for pairs of lists from `adict` and `b` for
       matching keys. if `b` is a list, we have to call `do_something`
       for all lists in `adict` with `b` as the common second
       argument.

    """
    if isinstance(b, dict):
        for key, value in adict.items():
            do_something(value, b[key])
    else:
        for key, value in adict.items():
            do_something(value, b)

def do_something(x, y):
    """x and y are lists"""
    pass

I am aware that this may not be a good design (Is it bad design to base control flow/conditionals around an object's class?). But writing two functions, one taking b as a dict and another as a list, seems too redundant. What are some better alternatives?


Solution

  • There's indeed a pattern for such problems, it's named "multiple dispatch" or "multimethods". You can find a (quite simple) example Python implementation here http://www.artima.com/weblogs/viewpost.jsp?thread=101605

    Using this solution, your code might look like:

    from mm import multimethod
    
    @multimethod(list, dict)
    def itersources(sources, samples):
        for key, value in sources.iteritems():
            yield value, samples[key]
    
    @multimethod(list, list)
    def itersources(sources, samples):
        for key, value in sources.iteritems():
            yield value, samples
    
    def fn(sources, samples):
        for value1, value2 in itersources(sources, samples):
            do_something_with(value1, value2)