Working with Numbers

This section discusses the operations on numbers in the Python standard library, such as generating random numbers, random sequences, encryption, etc.

Working with random numbers

Computers use pseudo-random number generators (PRNGs) to generate random numbers. It uses random seed value to derive a random number, typically used for statistical purposes, not to be used for security purposes. CSPRNGs (cryptographic secure pseudo-random number generators) are cryptographically secure PRNGs. CSRNGs are intended to be used for password generation, key generation, and other cryptographic purposes.

Python uses the random module to generate random numbers.

import random
print(random.random())

To implement a coin toss program:

import random
for i in range(10):
    print(random.choice(['heads', 'tails']))

We can specify the range of random numbers to be generated:

import random
print(random.randint(1, 10)) # to generate a random integer between 1 and 10
print(random.uniform(1, 10)) # to generate a random float between 1 and 10
print(random.randrange(1, 10, 2)) # to generate a random integer between 1 and 10, but skipping even numbers

We can use random.seed() to set the seed value. For a fixed seed value, we can generate the same sequence of random numbers.

import random
random.seed(1)
print(random.random())

No matter how many times we run the above program, the random number sequence will be the same. It is always 0.13436424411240122. This is useful for reproducing a result, but it is not a good idea for security purposes.

Random sequences operations

We can use choice function to pick a random element from a sequence, for example:

moves = ['rock', 'paper', 'scissors']
print(random.choice(moves))

The output is a random element from the sequence, for example 'paper'. Python 3.6 can use choices (with an s) to select multiple elements, and can use weights to specify the probability of each element, for example:

weights = [1, 2, 3] # weights for each element, probabilities are 1/6, 2/6, 3/6
print(random.choices(moves, weights, k=5)) # pick 5 elements

Elements will be selected with replacement by choices, but not by sample. This means the output could contain duplicate elements when using choices, but not when using sample.

print(random.sample(moves, 2))

shuffle is used to shuffle a sequence, for example:

random.shuffle(moves)
print(moves)

If the sequence is immutable, we can first use sample to pick some elements, and then use shuffle to shuffle the elements.

import string
result = random.sample(string.ascii_lowercase, k=len(string.ascii_lowercase))
random.shuffle(result)
print(result)

The output is a random permutation of the elements in the sequence.

Cryptographic random operations

Starting from Python 3.6, there is a secrets module that provides secure random number generators, such as generating passwords, keys, and so on. os module provides urandom function to read random bytes from the /dev/urandom device, which is the base of secrets module. The following code is to generate 16 bytes of random data using os module's urandom function:

import os
result = os.urandom(16)
print([hex(b) for b in result]) # convert bytes to hexadecimal

We don't need to use urandom because secrets provides higher-level random number generators, such as secrets.choice to pick a random element from a sequence, for example:

import secrets
print(secrets.choice(moves))

We can use secrets.token_bytes to generate a random byte sequence of a specified length:

import secrets
print(secrets.token_bytes(16))

we can use secrets.token_hex to generate a random hexadecimal string of a specified length:

import secrets
print(secrets.token_hex(16))

Temporary paswords and URLs

By using secrets.choice function, we can pick a random element from a sequence. It can be used to generate a temporary password.

import secrets
import string
results = "".join(secrets.choice(string.ascii_letters + string.digits) for i in range(8)) # generate a random password of 8 characters, including letters and digits
print(results)

The output is a random password of 8 characters, including letters and digits.

To enforce 1 number and 1 uppercase letter, use:

import secrets
import string
potentialChars = string.ascii_letters + string.digits + "!@#$%^&*()"
while True:
    results = "".join(secrets.choice(potentialChars) for i in range(8))
    if (any(c.isdigit() for c in results) and any(c.isupper() for c in results)):
        print(results)
        break

We can see the output is a random 8-character string with at least one number and one uppercase letter.

We can use secrets.token_urlsafe to generate a temporary, hard-to-guess URL-safe string:

result_url = "https://my.example.com/?reset=" + secrets.token_urlsafe(16)
print(result_url)

The output is a random URL-safe string like https://my.example.com/reset=?DyFTgxUM87r_TVoWTG4UZg. This function can be used to generate a temporary and safe url to user for a variety of purposes (e.g. resetting a password, sending an email, etc.).

Generating unique identifiers

We can use uuid module to generate unique identifiers. UUID stands for Universally Unique Identifier. UUIDs are 128-bit strings, which are guaranteed to be unique across all UUIDs.

uuid.uuid4 generates a pseudo-random UUID. The 4 represents the version number.

import uuid
result = uuid.uuid4()
print(result)
print(result.hex)
print(result.urn)

The output is something like

54885f36-a0a9-46a2-a745-1ff85a76c03b
54885f36a0a946a2a7451ff85a76c03b
urn:uuid:54885f36-a0a9-46a2-a745-1ff85a76c03b

hex method returns the hexadecimal representation of the UUID. The dashes are removed.

URN stands for Uniform Resource Name. It is a standard format for identifying resources.

uuid.uuid5 generates a UUID using a namespace and a name value. Here is an example:

import uuid
result = uuid.uuid5(uuid.NAMESPACE_DNS, "python.org")
print(result)
print(result.hex)
print(result.urn)

The output is something like

886313e1-3b8a-5372-9b90-0c9aee199e5d
886313e13b8a53729b900c9aee199e5d
urn:uuid:886313e1-3b8a-5372-9b90-0c9aee199e5d

This version of UUID is based on the MD5 hash function. It is a 128-bit hash value. UUID5 is not truly random. If we ran the same code twice, the output would be the same.

Statistics functions

Python standard library provides a set of functions to compute statistics. But if you are going to do some serious statistics, you should use numpy or pandas module.

statistics module provides functions to compute statistics, such as mean, median, mode, stdev, variance, etc.

An example of using statistics module:

import statistics
import random
random_list = [random.randint(1, 100) for i in range(10)]
print(statistics.mean(random_list))
print(statistics.median(random_list))
print(statistics.median_low(random_list))
print(statistics.median_high(random_list))
print(statistics.mode(random_list))
print(statistics.stdev(random_list))
print(statistics.variance(random_list))

The mean and median will return the mean and median of a random generated list. median_low and median_high are the lower and upper bounds of the median. Their returned values are guaranteed to be within the list. The mode function returns the most frequent value in the list. If there are multiple modes, it returns the one that is foremost in the list. The stdev and variance functions return the standard deviation and variance of the list.

Last updated