pytb.itertools module¶
Flexibly test a number possible configurations of a function¶
Assume you have a function that takes a number of parameters:
>>> def my_func(a, b, c=2, **kwargs):
... print(' '.join((a, b, c)), kwargs)
And you want to call it with multiple parameter combinations
>>> my_params = {
... 'a': 'a1',
... 'b': ('b1','b2'),
... 'c': ('c1', 'c2'),
... 'additional_arg': 'val'
... }
You can use the named_tuple()
function of this module to create any
possible combination of the provided parameters
>>> for params in named_product(my_params):
... my_func(**params)
a1 b1 c1 {'additional_arg': 'val'}
a1 b1 c2 {'additional_arg': 'val'}
a1 b2 c1 {'additional_arg': 'val'}
a1 b2 c2 {'additional_arg': 'val'}
Excluding some combinations¶
If some parameter combinations are not allowed, you can use the functions ability to work with nested dicts to overwrite values defined in an outer dict
>>> my_params = {
... 'a': 'a1',
... 'b': ('b1','b2'),
... 'c': {
... 'c1': {'b': 'b1'},
... 'c2': {},
... 'c3': {
... 'additional_arg': 'other val',
... 'another arg': 'yet another val'}
... },
... 'additional_arg': 'val'
... }
>>> for params in named_product(my_params):
... my_func(**params)
a1 b1 c1 {'additional_arg': 'val'}
a1 b1 c2 {'additional_arg': 'val'}
a1 b2 c2 {'additional_arg': 'val'}
a1 b1 c3 {'additional_arg': 'other val', 'another arg': 'yet another val'}
a1 b2 c3 {'additional_arg': 'other val', 'another arg': 'yet another val'}
Note that for c='c1'
only b='b1'
was used. You can also define
new variables inside each dict that only get used for combinations in
this branch.
safe_copy
of values¶
By default, all values are (deep) copied before they are yielded from the generator. This is really useful, as otherwise any change you make to an object in any combination would change the object for all other combination.
If you have large objects in your combinations however, copying may be
expensive. In this case you can use the safe_copy
parameter to control
if and which objects should be copied before yielding.
API Documentation¶
Methods to work with iterables conveniently. (methods that could be in the python stdlib itertools package)
-
pytb.itertools.
named_product
(values: Optional[Mapping[Any, Any]] = None, repeat: int = 1, safe_copy: Union[Sequence[str], bool] = True, **kwargs) → Generator[Any, None, None][source]¶ Return each possible combination of the input parameters (cartesian product), thus this provides the same basic functionality of :meth:
itertools.product
. However this method provides more flexibility as it:- returns dicts instead of tuples
>>> list(named_product(a=('X', 'Y'), b=(1, 2))) [{'a': 'X', 'b': 1}, {'a': 'X', 'b': 2}, {'a': 'Y', 'b': 1}, {'a': 'Y', 'b': 2}]
- accepts either a dict or kwargs
>>> list(named_product({ 'a':('X', 'Y') }, b=(1, 2))) [{'a': 'X', 'b': 1}, {'a': 'X', 'b': 2}, {'a': 'Y', 'b': 1}, {'a': 'Y', 'b': 2}]
- accepts nested dicts
>>> list(named_product( ... a=( ... {'X': {'b':(1,2)}}, ... {'Y': { ... 'b': (3, 4), ... 'c': (5, ) ... } ... } ... ) ... )) [{'a': {'X': {'b': (1, 2)}}}, {'a': {'Y': {'b': (3, 4), 'c': (5,)}}}]
- accepts scalar values
>>> list(named_product(b='Xy', c=('a', 'b'))) [{'b': 'Xy', 'c': 'a'}, {'b': 'Xy', 'c': 'b'}]
Parameters: - values – a dict of iterables used to create the cartesian product
- repeat – repeat iteration of the product N-times
- safe_copy – copy all values before yielding any combination.
If passed
True
all values are copied. If passedFalse
no values are copied. If passed an iterable of strings, only the values whose key is in the iterable are copied. - **kwargs – optional keyword arguments. The dict of
keyword arguments is merged with the values dict,
with
kwargs
overwriting values invalues