Generating lists populated with random objects For the purposes of unit-testing in Python, I've written a very simple function for generating variable-length lists populated with random objects:

constructors = [
    object,
    dict,
    lambda: bool(randint(0, 1)),
    lambda: randint(1, 1000),
    lambda: ''.join(choice(digits) for _ in range(randint(1, 10)))
]

choices = (
    lambda _: choice(constructors)(),
    lambda off: fake_list(off),
    lambda off: tuple(fake_list(off))
)

def fake_list(off = None):
    '''Creates a list populated with random objects.'''
    off = (off or 0) + 1
    return [choice(choices)(off) for _ in range(max(randint(1, 10) - off, 0))]


List generated with this function typically look something like this:

[(269, '6'), [{}, 990, 347, &lt;object object at 0x7f51b230b130&gt;, {}, [{}, '91921063', 302, '0047953', {}, ()], True], '70262', True]


Do you see any problems with it? Is there a more efficient/elegant way of doing this that you could suggest?

Quality of the generated primitives (strings, integers) is not of priority (i.e. string.digits should be enough).
Requirements may vary so I find it hard to use with the "hardcoded" constructors. Instead I would define a function that accept such constructors as an argument.

I would also define the function returning the list-comprehension as an inner function so the off parameter is hidden from the public interface (the end-user does not need to know such implementation details). Besides, off is a terrible name for such variable when all it does it to track the recursion depth, better call it depth, then.

An other thing that could be worth taking as parameter (even though a sensible default would make sense most of the time) is the maximum length of the inner lists (and thus the maximum allowed recursion level):

import random
from string import digits


def fake_list(items, max_size=10):
    """Create a list populated with random objects constructed from `items`"""

    choices = (
            lambda _: random.choice(items)(),
            lambda depth: random_list(depth),
            lambda depth: tuple(random_list(depth)),
    )

    def random_list(depth):
        return [
                random.choice(choices)(depth + 1)
                for _ in range(max(random.randint(1, max_size) - depth, 0))
        ]

    if max_size &lt; 1:
        raise ValueError('minimal size of output should be at least 1')

    return random_list(0)


def test():
    constructors = [
        object,
        dict,
        lambda: bool(random.randint(0, 1)),
        lambda: random.randint(1, 1000),
        lambda: ''.join(
            random.choice(digits)
            for _ in range(random.randint(1, 10))),
    ]

    print(fake_list(constructors))


if __name__ == '__main__':
    test()


That way I can call:

fake_list([lambda: float('-inf'), lambda: range(random.randint(-100, -10), random.randint(10, 100)), set])


if I need to.