An Arguments Container pattern

In a comment on my earlier post A Globals Class pattern for Python, Mike Müller wrote
“No need for globals. Just explicitly pass your container. In my opinion this is much easier to understand.”

Mike’s comment led me to some further thoughts on the subject.

Suppose you have a number of things — x, y, and z — that you want to make available to many functions in a module.

There are four strategies that you could use. You could

1. pass x, y, and z as individual arguments
2. make x, y, and z globals

or you could create a container C of some sort and

3. pass container C as an argument
4. make container C a global

So you have two basic questions to answer. When you make the things — x, y, and z — available:

A. Do you make them available in global variables, or in arguments that you pass around?

B. Do you make them available individually, or do you put them in some kind of container and make the container available?

My original post assumed that in at least some situations you might answer question A with “use global variables” and then went on to propose that in those situations the best answer to B is “put them in a container”.

Since the point of that post was to point out the usefulness of a class as a container, I called the proposed pattern the Globals Class pattern. But in most cases some other kind of container would do as well as a class. I could almost as easily have called the pattern the Globals Container pattern.

So if you look at these two questions — A and B — I think it is interesting where Mike and I differ, and where we agree.

Question A: args or globals

Where we differ, if you could call it that, is in the answer to A.

Mike wrote “No need for globals. Just explicitly pass your container. In my opinion this is much easier to understand.”

In my post I wrote “Sometimes globals are the best practical solution to a particular programming problem.” But that wasn’t really what the post was about. It was about the answer to question B.

So I can’t really say that Mike and I disagree very much. He says “I like apples”. I say “Sometimes I like an orange.”  No big deal.

Question B — multiple things or a single container

What is much more interesting is that we both agree on the answer to question B: use a container object.

But since I was talking about globals, I was talking about a container for globals.  Since Mike was talking about arguments, he was talking about a container for arguments.

Which means that we have two different patterns. My earlier post was about strategy 4 — a Globals Container pattern. Mike is talking about strategy 3 — what we might call an Arguments Container pattern.

As it happens, I had stumbled onto the Arguments Container pattern myself, not in Python but in Java. The circumstances were very similar to the circumstances that led to the Python Globals Class pattern. I had a lot of variables that I needed to pass around. As the code evolved,the argument lists got longer and harder to manage. Finally I just bundled all of the variables into a single container object and passed the container around. As I needed to add new arguments, I was able to add them to just one place — the container.

At the time, I felt sort of stupid doing this. I hadn’t ever heard of this as a programming technique.  It smacked of sneaking global variables in through the back door, and of course everybody knows that globals are always bad. But it worked, and it made my life a lot easier.

So now Mike comes along and proposes doing exactly the same thing. I feel relieved. I’m not the only one doing this. It may even be a Good Thing.

So I’m happy to announce — not the discovery, certainly — the christening of the Arguments Container pattern, which says, basically:

Sometimes when you have a lot of individual variables that you need to pass around to a lot of different functions or methods, the best solution is to put them into a container object and just pass the container object around.

This is not a specifically Python pattern. And in a way it is No Big Deal. But I’m doing a bit of shouting and arm-waving here because I think that somewhere there is probably at least one person for whom this post might be useful.

5 thoughts on “An Arguments Container pattern

  1. And, of course, nowadays the easiest container to use would probably be a named tuple from the collections library

    >>> import collections
    >>> x = collections.namedtuple("myType", "a b c d e")
    >>> x1 = x(5, 4, 3, 2, 1)
    >>> x1.a
    5
    >>> x1[0]
    5
    >>> x1[4]
    

    1
    >>> x1.e
    1
    >>>

  2. Damn! Steve H. pipped me to the post🙂

    I should also add that sometimes more than one container might be best when you start grouping; and the reason for those groupings might begin to appear.

  3. The idiomatic way to do what you’re describing in Python is to put those needed-by-lots-of-things as attributes of a module.

    Then, whatever needs them can import that module (e.g. ‘import config’) and everything has access to the objects that way: ‘do_something_with(config.foo)’.

    That way, there’s no need for any ‘global’ statement, there’s no confusion over why a class is being defined but not instantiated, etc.

  4. There is another way to solve your problem, which is to put the functions that need access to the variables into a class, and make the variables attributes of the class. If the functions are related this can be a very clean solution.

    What you call an arguments container is not new. Martin Fowler calls it a parameter object in his book Refactoring.

  5. Firstly, I am opposed to globals because they extend the variables supplied to a function, making testing, verification and comprehension much harder.

    Secondly, I make an exception fore read-only variables:
    FILEMASK = ‘trace%s.txt’

    Thirdly, I revoke that exception if the constant might become a real variable in later development:
    COUNTRYCODE = ‘us’

    Now to Question A. Imagine you are retro-fitting existing code and adding the container as a parameter to existing functions. Users outside the module that call any of these functions will have to be modified, and will have to pass in the container. Now this container member is potentially changed anywhere in the entire code base.

    Instead, making it a private global within the module limits the scope of where its members are changed:
    _gbl = Mem()

    If you argue that other modules may have a need to change its values, I would respond that these values should, instead, live in a module of their own called gbls.py (say), making it explicit what values are truly program-wide global.

Comments are closed.