Is it better practice to pass sometimes complex dicts for parameters instead of parameter objects?
Question: I've been programming Python for a year now, having come from a Java
background, and I've noticed that, at least in my organization, the style for
passing complex parameters to functions is to use dicts or tuples, rather than
instances of a specialized parameter class. For example, we have a method that
takes three dicts, each structured in a particular way, each of which is
itself formatted as tuples. It's complicated for me to build args and to read
the code. Here's an example of a passed arg:

    
    
    {'[A].X': ((DiscreteMarginalDistribution, ('red', 'blue')), ()),
     '[A].Y': ((DiscreteConditionalDistribution, ('yellow', 'green'), ('red', 'blue')),
               (IdentityAggregator('[A].X'), ))
    

My questions are:

  1. Is passing dicts/tuples like this a common Python idiom?
  2. When, if ever, do you write Python code to use the latter (parameter instances)? E.g., when the nested structure surpasses some complexity threshold.

Thanks in advance!


Answer:   * Yes, it is very usual to pass a dictionary to Python functions in order to reduce the number of arguments. Dictionary-style configuration with proper key naming is much more readable than just using tuples.

  * I consider it rather uncommon to dynamically construct dedicated instances of a custom config class. I'd stick with dictionaries for that. In case your config dict and the consumer of it go out of sync, you get `KeyError`s, which are pretty good to debug.

**Some recommendations and reasoning:**

If some parts of your application require really really complex configuration,
I consider it a good idea to have a configuration object that properly
represents the current config. However, in my projects I never ended up
passing such objects as function arguments. This smells. In some applications,
I have a _constant_ global configuration object, set up during bootstrap. Such
an object is globally available and treated as "immutable".

Single functions should never be so complex that they require to retrieve a
tremendously complex configuration. This indicates that you should split your
code into several components, each subunit having a rather simple
parameterization.

If the runtime configuration of a function has a somewhat higher complexity
than it is easily dealt with normal (keyword)arguments, it is absolutely
common to pass a dictionary, so to say as a "leightweight" configuration
object. A well thought-through selection of key names makes such an approach
very well readable. Of course you can also build up a hierarchy in case one
level is not enough for your use case.

Most importantly, please note that in many cases the best way is to explicitly
define the parameterization of a function via its signature, using the normal
argument specification:

    
    
    def f(a, b, c, d, e):
        ...
    

In the calling code, you can then prepare the values for these arguments in a
dictionary:

    
    
    arguments = {
        a = 1,
        b = 2,
        c = 3,
        d = 4,
        e = "x"
    }
    

and then use Python's snytactic sugar for keyword expansion upon function
call:

    
    
    f(**arguments)
    

