Whatever you call it, a few wonderfully useful features in Python are
a, b = 1, 2
foo(*args)
,foo(**kwargs)
.And, with a similar syntax (*args
and **kwargs
), functions may be defined to accept an unknown number of
positional or keword arguments!
=
Unpack any fixed length iterable into the same number of names:
>>> a, b, c = (1, 2, 3)
>>> a, b, c
(1, 2, 3)
>>> d, e, f = [1, 2, 3]
>>> d, e, f
(1, 2, 3)
>>> g, h, i = '123'
>>> g, h, i
('1', '2', '3')
But, what happens with a dictionary since key iteration is not predictably ordered?
>>> j, k, l = {'j': 1, 'k': 2, 'l': 3}
>>> j, k, l
('k', 'j', 'l')
That’s awesome! And, remember that generators are iterables, too:
>>> def gen():
... for x in range(3):
... yield x
>>> m, n, o = gen()
>>> m, n, o
(0, 1, 2)
But don’t forget that the number of names must equal the number of items being expanded:
>>> blow, up = 3, 2, 1
Traceback (most recent call last):
...
ValueError: too many values to unpack
>>> blow, up = 3,
Traceback (most recent call last):
...
ValueError: need more than 1 value to unpack
*args
and **kwargs
First, we’ll need a function to play with and some values to unpack into it.
>>> def foo(a, b, c): return c, b, a
>>> tuple123 = (1, 2, 3)
>>> list123 = [1, 2, 3]
>>> str123 = '123'
>>> dict123 = {'a': 1, 'b': 2, 'c': 3}
Excellent; let’s unpack! Everything from the previous section still applies, except that instead of the iterable
following =
, it follows the *
operator (splat!).
>>> foo(*tuple123)
(3, 2, 1)
>>> foo(*list123)
(3, 2, 1)
>>> foo(*str123)
('3', '2', '1')
>>> foo(*dict123)
('b', 'c', 'a')
>>> # and unpacking generators? of course.
>>> foo(*gen())
(2, 1, 0)
Let’s revisit foo(*dict123)
… what if we wanted to apply the dictionary items as keyword arguments?
>>> foo(**dict123)
(3, 2, 1)
Nice! And, since we’re passing values as keyword arguments, it doesn’t matter that the iteration order is unpredictable.
*args
and **kwargs
Now let’s go the opposite direction. Instead of expanding arguments when calling functions, let’s define functions that collect positional arguments into an iterable and keyword arguments into a dictionary!
>>> def bar(*args): return args
>>> def baz(**kwargs): return kwargs
>>> bar(1, 2, 3)
(1, 2, 3)
>>> baz(a=1, b=2, c=3)
{'a': 1, 'c': 3, 'b': 2}
That’s enough for now. Check out the official Python tutorial for more on defining functions.
A common use for unpacking is the ability to neatly swap values between names, or within in a dictionary or list, without a temporary variable:
>>> a, b = 1, 2
>>> a, b
(1, 2)
# now swap their values
>>> a, b = b, a
>>> a, b
(2, 1)
# swap positions in a list:
>>> list123 = [1, 2, 3]
>>> list123[0], list123[2] = list123[2], list123[0]
>>> list123
[3, 2, 1]
# swap keys in a dictionary
>>> dict123 = {'a': 1, 'b': 2, 'c': 3}
>>> dict123['a'], dict123['c'] = dict123['c'], dict123['a']
>>> dict123
{'a': 3, 'c': 1, 'b': 2}
And that’s all I’ve got for this week. With a little luck, next week I’ll talk about slicing … but PyCon 2013 is right around the corner and it might inspire a completely different topic!