Software Engineering – Week the Sixth
Python syntax: it doesn’t always make sense. I thought I knew Python argument unpacking, I really did, but this week in Software Engineering I got “schooled” as they say–I missed a quiz question. Every language has a certain set of rules from which you can extrapolate knowledge about other parts of the language–grammar rules and spelling conventions are two things to immediately come to mind as things that spoken language rely on for ease-of-learning.
I like Python because although it does some things differently, it mostly seems to do things that make sense. For example, parentheses are used for order of operations when evaluating compound expressions, lists are separated by commas, and calling conventions look much like how they do in C, C++, Java… the list goes on. That gets to the point: there is a way to pass multiple positional and/or keyword arguments (arguments specified by argument name) at a time from an iterable item. Positional arguments are positional. A tautology is a tautology.
Here’s an example of passing arguments individually in a call: func(1, 2, 8, arg4=2, arg8=1)
Keyword arguments can be specified out of order so it makes sense for them to be passed after all the positional arguments are passed (so they don’t displace the positional argument position).
func(1, 2, arg4=2, arg8=1, 8) would not be valid.
So why am I complaining? This seems sane.
Think back to first grade math: definition of an expression in a parenthesis (9 + 2)*8. It is deemed correct to do the 9 + 2 first. Now, let’s make this pre-fix notation rather than in-fix: *(+ 9 2) 8. Let’s look at the argument unpacking syntax:
func(*<iterable of positional args>)
so it seems that * is an operator. Maybe something like this:
func(*[1, 2, 6]) -> func(1, 2, 6)
Wrong.
* is not an operator. Why not? It doesn’t behave like one. What is it? It’s a magic keyword that tells the function to do some extra magic parsing. Here’s the Python-valid expression which breaks my mind:
func(a = 5, *[1, 2, 6])
If * were an operator, it would evaluate to:
func(a = 5, 1, 2, 6) an invalid expression because remember that keyword args are passed after positional args. Instead, this is perfectly fine, instead evaluating to func(1, 2, 6, a = 5). If you think about unpacking as an operation, this is the equivalent to Python evaluating func(1 + 4, 2 + 9) as something like: func(3, 13) or some example other than func(5, 11). Long story short: unpacking is not the work of an operator.
How could I not know this after passing arguments by unpacking for 7 years? It’s because I’ve never tested the edge case where packed-positionals are passed after keywords. Perhaps this makes me a bad programmer, but just like with the English language, I rely on _some_ amount of consistency to survive.
In summary, this week, I found Python’s example of the word “weird”: it seemingly breaks conventions and it’s something I just have to commit to memory as one of those great mysteries of the modern world.
2/23/2014