In this article, we are going to generate random values things using Python. The list of contents is below.
- What is random?
- random module in Python
- How to get a random boolean in Python?
- How to get a random letter in Python?
- How to get N random items from a sequence in Python?
- How to get N random and unique items from a sequence in Python?
- How to get a random number between integer range in Python?
- How to get a random number between float range in Python?
- How to get a random string in Python?
- How to get a cryptographically secure random string in Python?
- How to get a random row through SQLAlchemy?
- How to get a random row through Django ORM?
random - happening, done or chosen by chance rather than according to a plan
In our programs we often generate random numbers, characters, hashes, etc... But are they truly random with taking the fact that computers are deterministic? or how do computers produce random values?
In computer science, there are several random number generators and the most common one is a pseudorandom number generator(PRNG) which generates numbers that look random. The disadvantage of PRNG is that the numbers that it generates are deterministic and can be reproduced if the state of the PRNG is known.
Another type of generator is the hardware random number generator (HRNG) or true random number generator (TRNG). It is a device that generates random numbers from a physical process, rather than by means of an algorithm(like PRNG). The disadvantage of HRNG is that it relies on an external device and not very fast.
However, carefully designed cryptographically secure pseudo-random number generators (CSPRNG) also exist, with special features specifically designed for use in cryptography.
It's important to note that pseudorandom number generators should not be used for security purposes, cryptographically secure pseudorandom number generators should be used instead.
In this article, we will go over some examples of generating both predictable and cryptographically secure random values using Python.
We will use Python's random module a lot. Some facts about the module:
- Module implements pseudorandom number generators.
Almost all module functions depend on the basic function
- Python uses the Mersenne Twister as the core generator.
- The pseudo-random generators of this module should not be used for security purposes.
random() function generates a random float uniformly in the semi-open range [0.0, 1.0).
>>> import random >>> random.random() 0.7320183271158612
random holds a state that is being used in the generation and it is being changed "every time" we use
There is a
getstate() function that returns an object capturing the current internal state of the generator. This object can be passed to
setstate(state) later to restore the state.
>>> random.getstate() (3, (2147483648, 3487414761, 1383097352, 2873053358, 653991483, 14937...
If the state is the same then the generated numbers will be equal.
>>> state = random.getstate() # <--- keeping the current state >>> first_random = random.random() # <--- generating a random float >>> first_random 0.8147563911510902 >>> state == random.getstate() # <--- checking the state change False >>> second_random = random.random() >>> second_random 0.4963458268419496 # <--- state is different so the number is also different >>> first_random == second_random False >>> random.setstate(state) # <--- changing state back to the first one >>> third_random = random.random() >>> third_random 0.8147563911510902 >>> first_random == third_random True # <--- same number was generated >>> forth_random = random.random() >>> second_random == forth_random True # <--- continuance is repeated
As you can see, the result of the
random() function depends on the current state of the
There are many ways to get a random boolean in Python.
Comparing the result of
random() with a half:
>>> random() < 0.5
choice(seq) function which returns a random element from a non-empty sequence:
>>> random.choice([True, False])
getrandbits(k) function which returns a Python integer with k random bits. If
k=1 we will get 0 or 1 and can coerce it to boolean:
>>> random.getrandbits(1) == 1
string module comes to help us with this problem.
ascii_letters holds all letters that we can choose from:
>>> import string >>> string.ascii_letters 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
That could be a sequence for the
choice(seq) function to choose from:
>>> random.choice(string.ascii_letters) 'H'
Another way to achieve this is by using the combination of
randrange(start, stop[, step]).
chr(i) - returns the string representing a character whose Unicode code point is the integer i.
randrange(start, stop[, step]) - returns a randomly selected element from
range(start, stop, step).
ascii for the letter 'a' is 97 and there are 26 letters in the alphabet, so the range will be from 97 to 97 + 26:
>>> chr(random.randrange(97, 97 + 26)) 'p'
We already did this in the previous example. Strings are also sequences and we got a random letter from a string. But now we want to get more than one randomly selected item from a sequence.
choices() can help us with that:
>>> population = [1, 2, 3, 4, 5, 6, 7] >>> random.choices(population, k=3) # <--- getting 3 randomly selected items from the list [5, 1, 1] # <--- integer 1 appears more than once
Note that choices could be duplicated. In the next section, we will show an example of getting N length of unique elements chosen from a sequence.
sample(population, k) function, which returns a k length list of unique elements chosen from the population sequence or set:
>>> population = [1, 2, 3, 4, 5, 6, 7, 8, 9] >>> random.sample(population, k=5) [2, 1, 5, 3, 6]
Note that the sequence that we feed to sample function doesn't contain any duplicate elements, but if it does sample could return a list with duplicate values in it:
>>> population = [1, 1, 1, 2, 2, 2, 5] >>> random.sample(population, k=3) [1, 2, 1]
We can convert the
population to a set to have a strictly unique collection of randomly selected items in the end:
>>> population = [1, 1, 1, 2, 2, 2, 5] >>> random.sample(set(population), k=3) [5, 2, 1]
Let's say we want to generate a random integer between 0 and 9. Using
randint() function is the easiest way in this case:
>>> random.randint(0, 9) 3
>>> random.randrange(0, 9 + 1) 5
also, we can create a list of integers between 0 and 9 and use the
choice() function passing that sequence:
>>> integers = [n for n in range(10)] # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> random.choice(integers) 8
uniform(a, b) function, which returns a random floating point number N such that a <= N <= b for a <= b and b <= N <= a for b < a:
>>> random.uniform(1.1, 8.2) 4.443894738120874
string module contains collection of upper case and lower case letters and also digits. We can use them to generate k-length random strings.
using only uppercase letters:
>>> import string >>> string.ascii_uppercase 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' >>> ''.join(random.choices(string.ascii_uppercase, k=k)) 'CGLLZLK'
using only uppercase and lowercase letters with digits:
>>> string.ascii_lowercase 'abcdefghijklmnopqrstuvwxyz' >>> string.digits '0123456789' >>> collection = string.ascii_uppercase + string.ascii_lowercase + string.digits >>> ''.join(random.choices(collection, k=k)) '0fN7SRr'
As we noted at the beginning, these ways to generate strings are not secure. In the next section, we will see the way to generate secure strings.
secrets module(new in version 3.6) is used for generating cryptographically strong random values suitable for managing data such as passwords, account authentication, security tokens, and related secrets.
>>> import secrets >>> ''.join([secrets.choice(collection) for _ in range(k)]) 'Wzhs4i8'
Data generated using
secrets module should be unpredictable enough for cryptographic applications, though its exact quality depends on the OS implementation.
Most databases have the ability to order rows by a random function:
>>> from sqlalchemy.sql.expression import func, select >>> select.order_by(func.random()) # <--- PostgreSQL, SQLite >>> select.order_by(func.rand()) # <--- MySQL >>> select.order_by('dbms_random.value') # <--- Oracle
and we need to limit the number of results to 1.
Ordering also works here. Passing
order_by() method will randomly order rows and all we need is to select only one row:
order_by('?') queries may be expensive and slow, depending on the database backend you’re using.
If you have millions of rows, getting the count of rows and using it to generate a random limit and offset will be more efficient:
>>> from django.db.models.aggregates import Count >>> count = MyModel.objects.aggregate(count=Count('id')]['count'] >>> random_index = random.randint(0, count - 2) >>> row = MyModel.objects.all()[random_index:random_index + 1]
The disadvantage of this approach is, it's 2 SQL queries and if the count changes in between, it might be possible to get an out of bounds error.
However, it will execute faster compared with database ordering in some cases.
There are many other ways to generate random values using Python. We tried to put here the most common ones. If you have any interesting solutions or ideas to generate random values, feel free to comment them below.