In the past my quick tutorials are mainly focused on advanced or weird issues that I had trouble figuring out on my own. I’ve decided to shake that up a bit and do a new tutorial series: ELI5 Tutorials. For these tutorials I am going to go over some more common features of python that I feel people tend to explain too abstractly for novice programmers. I’m going to start my series with something pretty simple but very unintuitive: *args and **kwargs.
What are *args and **kwargs? Quick Answer
I find the best way to learn is with an example, so lets jump right in:
def showGrades(name, english, german): print(f"{name} got the following grades:") print(f"English: {english}) print(f"German: {german})
The function above is used to display someone’s grades. it takes name, email and two grades as the input. Here is an example of it in use:
# Note: my wordpress does not parse angle brackets properly, so I am using --- to indicate a console command --- showGrades("Roy", english= "A", german= "B") "Roy got the following grades:" "English: A" "German: B"
As you can see, I used a mix of arguments and keyword arguments when I called that function. The quick answer to the question is that *args is a list of all arguments, while **kwargs is a dictionary of all keyword arguments where the key is the keyword name as a string.
How would I use these?
In the example above, you could use these as so:
def showGrades(*args, **kwargs): print(f"{args[0]} got the following grades:") for subject, grade in kwargs.items(): print(f"{subject.title()}: {grade}")
Which would return the exact same result as above.
--- showGrades("Roy", english= "A", german= "B") "Roy got the following grades:" "English: A" "German: B"
This however isn’t the best way to write this specific example. let’s refactor it to make it a little clearer:
def showGrades(name, **kwargs): print(f"{name} got the following grades:") for subject, grade in kwargs.items(): print(f"{subject.title()}: {grade}")
The reason I would suggest this is that *args and **kwargs are best used when there is a variable amount of input. In our example, we always want a name while the amount of subject + grade pairs can vary. In fact, we can make it slightly clearer as follows:
def showGrades(name, **grades): print(f"{name} got the following grades:") for subject, grade in grades.items(): print(f"{subject.title()}: {grade}")
You see, what is important is the * and ** parts of the variable names. We can actually name them what we want, *args and **kwargs is just the common convention.
When would I use these?
The best place to use these is when there could be an unknown amount of variables in a function. In the previous example we don’t know what or how many subjects a person is studying, so using **grades allows us to handle any number of subjects.
Why not use list/dictionaries as the variables?
This is a very good question since *args and **kwargs are basically just packing the relevant parts of the call into lists and dictionaries respectively. In fact, while my example is good to show how **kwargs work, in practice you would probably be better off using a dictionary as the second argument. In terms of a practical example, check out this code for timing the execution of a function:
import datetime def timeFunction(function, *args, **kwargs): start = datetime.datetime.now() output = function(*args, **kwargs) executionTime = datetime.datetime.now() - start return executionTime, output
In this case, we are passing a function and arguments for that function into timeFunction. This is going to compute that answer and return it along with how long it took to run. This kind of function requires the open-ended *args and **kwargs terms as the function it is timing could take any number of arguments.
Conclusion
So there you have it: an introduction to *args and **kwargs along with an explanation of when they are useful. In upcoming ELI5s I will reference more cases where *args and **kwargs are useful, so keep an eye out!