next(i for i in itertools.imap(lambda x: random.randint(p,q)|1,itertools.count()) if isPrime(i))
It starts with itertools.count () - it gives an infinite set.
Each number maps to a new random number in the range, itertools.imap (). imap is like a map, but returns an iterator, not a list - we donβt want to generate a list of elusive random numbers!
Then the first matching number is found and returned.
It works effectively even if p and q are very far apart - for example, 1 and 10 ** 30, which do not generate a complete list!
By the way, this is no more efficient than your code above, and it is much harder to understand at a glance - please note that the next programmer should read your code and just do it as you did above. This programmer may be to you in six months, when you forgot what this code was supposed to do!
PS - in practice, you can replace count () with xrange (range NOT!), For example. xrange((pq)**1.5+20) to make no more than the number of attempts (balanced between a limited test for small ranges and large ranges and no more than 1/2% probability of failure, if it can be successful), otherwise as suggested in another post, you can loop forever.
PPS - improvement: replacing random.randint(p,q) with random.randint(p,q)|1 - this makes the code twice as efficient, but eliminates the possibility that the result will be 2.
source share