def random_product(items, num=None, rng=None):
    import utool as ut
    rng = ut.ensure_rng(rng, 'python')
    seen = set()
    items = [list(g) for g in items]
    max_num = ut.prod(map(len, items))
    if num is None:
        num = max_num
    if num > max_num:
        raise ValueError('num exceedes maximum number of products')
    if num > max_num // 2:
        for prod in ut.shuffle(list(it.product(*items)), rng=rng):
            yield prod
    else:
        while len(seen) < num:
            idxs = tuple(rng.randint(0, len(g) - 1) for g in items)
            if idxs not in seen:
                seen.add(idxs)
                prod = tuple(g[x] for g, x in zip(items, idxs))
                yield prod