Python's extra-clever function parameters
I'm not sure where I ran across this, but until recently I wasn't aware that you could pre-explode lists and tuples when you declared what parameters a function takes. That sounds stilted, so I'll give an example:
def foo(a, (b, c, d)): print a, b, c, d
This is slightly more compact than the equivalent:
def bar(a, tmp): b, c, d = tmp print a, b, c, d
Contrary to what it might first look like, foo
's second argument
doesn't have to be an explicit list or tuple with three elements.
Instead, it can be anything that will yield exactly three values when
iterated. Want to pass xrange(0, 3)
, or even {1: 10, 2: 20, 3: 30}
?
No problem.
(Iterating a dictionary returns the keys (in some random order, so this is not necessarily a useful example).)
This freedom comes with a small drawback. Compare:
>>> foo(10)
TypeError: foo() takes exactly 2 arguments (1 given)
>>> foo(10, (1, 2))
ValueError: unpack tuple of wrong size
(I have elided the tracebacks.)
This is different from a language like Haskell, which has a more pattern-matching approach to this; I believe in Haskell you would get an 'argument mismatch' type of error in both cases. (Haskell looks like the kind of mind-bending neat language that's worth reading about, even if you never write a Haskell program.)
Writing this made me curious enough to look at the actual
bytecodes generated for foo
and bar
(with the dis module). It turns
out that they're pretty much identical, because foo
starts with
a prequel bit to explicitly unpack an explicit but internal second
argument into b
, c
, and d
. So foo
is pretty much just syntactic
sugar, supported by the bytecode compiler, for what bar
writes out
explicitly.
Although I've used this in code recently, I'm not sure it's entirely a good idea. Sometimes I feel that Python is a little bit too clever for my own good, and that I would be better off if I could resist the temptation to use some of these neat features.
(PS: yes, you can nest pre-exploded parameters. Don't go there.)
|
|