How to make function's arguments optional as a group?

I was just wondering what would be the preferred way in Python to make a group of arguments of a function optional, but only as the whole group. Meaning: they have to either all be given, or none.

For an example, let's say I want to make a print function, that takes a message string as first positional argument and optionally a file-like object and an encoding as second and third arguments.

Now I want this function to print to stdout if no file is given, and to the file otherwise. The tricky bit is this: I want this function to always require an encoding to be specified whenever a file is used. And calling this function with an encoding, but no file should also be forbidden.

In Java, I could overload the function and give implementations for both valid variants:

public void print(string message);
public void print(string message, File f, string encoding);

This allows me to call this function in exactly the two ways I want to be possible, with either one or all three arguments.

In Python, I can make single arguments optional by supplying a default value, but I cannot group them together.

def print(msg, file=None, encoding=None)

allows me to call the function by providing a message and none, both or just any one of the other parameters:

print("test")
print("test", file=someFile)
print("test", encoding="utf-8")
print("test", file=someFile, encoding="utf-8")

These are all valid calls to the Python declaration above, even though with my implementation, setting an encoding or file without the other one might make no sense.

I am aware that I could simply check both optionals for an invalid default value and raise an Exception at runtime whenever I find only one is set, but I think that is bad for a couple of reasons:

  1. The Exception is raised only if the invalid call is executed, so it might not occur during testing.
  2. I have no way of telling that both parameters are required as a pair by just looking at the declaration or an auto-generated quick reference without diving into the implementation.
  3. No code analysis tool would be able to warn me about an invalid call.

So is there any better way to syntactically specify that a number of optional arguments are grouped together?


Solution 1:

Python is not supporting overloading methods. And there is not a really good way to simulate an overloading design. So best you can do is using if statements with different arguments. Like you do in your method.

Or you can use **kwargs as argument and use if only the desired argument is defined.

def a_very_important_method(**kwargs)
    if kwargs["arg1"] is not None:
        # logic
    if kwargs["arg2"] is not None:
        # another logic

a_very_important_method(arg1="value1", arg2="value2")