 # Generating random things using Python

03, May 2020 - 8 min read

#### Introduction ☜

In this article, we are going to generate random values things using Python. The list of contents is below.

#### What is random? ☜

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.

#### random module in 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 `random()`.
• 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 `random()`. 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 `random`.

#### How to get a random boolean in Python? ☜

There are many ways to get a random boolean in Python.

Comparing the result of `random()` with a half:

``>>> random() < 0.5``

Using `choice(seq)` function which returns a random element from a non-empty sequence:

``>>> random.choice([True, False])``

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

``>>> bool(random.getrandbits(1))``

or:

``>>> random.getrandbits(1) == 1``

#### How to get a random letter in Python? ☜

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 `chr(i)` and `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'
``````

#### How to get N random items from a sequence in Python? ☜

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.

#### How to get N random and unique items from a sequence in Python? ☜

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

#### How to get a random number between integer range in Python? ☜

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

or using `randrange()`:

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

#### How to get a random number between float range in Python? ☜

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

#### How to get a random string in Python? ☜

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

#### How to get a cryptographically secure random string in Python? ☜

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

#### How to get a random row through SQLAlchemy? ☜

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.

#### How to get a random row through Django ORM? ☜

Ordering also works here. Passing `?` to `order_by()` method will randomly order rows and all we need is to select only one row:

``````>>> MyModel.objects.order_by('?').first()
``````

Note: `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.

#### Conclusion ☜

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.