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
```