The function definition below comes from the itertools recipes page
(https://docs.python.org/2/library/itertools.html#recipes).
def random_product(*args, **kwds):
"Random selection from itertools.product(*args, **kwds)"
pools = map(tuple, args) * kwds.get('repeat', 1)
return tuple(random.choice(pool) for pool in pools)
The function signature would seem to be more readable written with the “repeat” argument specified explicitly, e.g.
def random_product(*args, repeat=1)
But I’m guessing there is a reason why it is written using the general idiom. Can anyone explain?
1
In Python 2, you cannot put a keyword argument after *args
, that’s a syntax error:
>>> import sys
>>> sys.version_info
sys.version_info(major=2, minor=7, micro=8, releaselevel='final', serial=0)
>>> def random_product(*args, repeat=1):
File "<stdin>", line 1
def random_product(*args, repeat=1):
^
SyntaxError: invalid syntax
If you put it before the positional catch-all, then it captures the first positional argument:
>>> def random_product(repeat=1, *args): return repeat, args
...
>>> random_product('foo', 'bar')
('foo', ('bar',))
In Python 2 then, your only option is to capture arbitrary keyword arguments with **kw
(since that only ever captures actual keyword arguments, not positional arguments), and extract repeat
from that.
Python 3 adjusted what is supported; there any keyword arguments specified after the *args
catch-all are allowed and are seen as keyword-only:
>>> import sys
>>> sys.version_info
sys.version_info(major=3, minor=4, micro=1, releaselevel='final', serial=0)
>>> def random_product(*args, repeat=1): return args, repeat
...
>>> random_product('foo', 'bar')
(('foo', 'bar'), 1)