The objects produced by itertools.product()
are iterable but do not implement many other Collections methods. However, since the iterables that the product is run over are stored as tuples in self.pools
it is relatively straightforward to determine in advance the length of this iterable. Similarly the nth item in this iterable can be efficiently determined without having to pass through the first n terms and a tuple is contained in this sequence if and only if each of it’s terms is contained in the iterables that this product is run over. For example:
class ProductObject:
# Note self.pools is a list of tuples.
...
def __len__(self):
return math.prod([len(pool) for pool in self.pools])
def __contains__(self, item):
# We should also check that item is a tuple of length len(self.pools)
return all(x in pool for x, pool in zip(item, self.pools))
def __getitem__(self, index):
result = []
for i in range(len(self.pools)):
size = math.prod([len(pool) for pool in pools[i+1:]])
result.append(self.pools[i][index // size])
index -= size * (index // size)
return tuple(result)
This would allow the following to be done:
>>> X = product(range(5), 'abc')
>>> len(X)
15
>>> X[5]
(1, 'c')
>>> (7, 'x') in X
False
Of course this should be implemented at the C level for performance, but by doing this and adding:
__contains__
__len__
__getindex__
this would make itertools.product objects
instances of collections.abc.Sequence
.
Note, it is also relatively straightforward to implement __reversed__
so that we can also do:
>>> reversed(X)[1] == X[-2]
True