One-time pad encryption using letter shifting This program asks the user to input a List of words. It then returns a list of encrypted words, and a second list which specifies the numerical shifts used to encrypt those words. The idea was obtained from https://www.khanacademy.org/computing/computer-science/cryptography/crypt/v/one-time-pad

import random

def rotate_letter(letter, number):
    if letter == ' ':
        return letter
    if letter.islower():
        start = ord('a')
    elif not letter.islower():
        start = ord('A')

    normal_shift = ord(letter) - start
    new_letter = start + (normal_shift + number) % 26
    return chr(new_letter)

def avoid(letter):
    avoid = ' ,!?¿,*+-%/@1234567890'
    for i in avoid:
        if i == letter:
            return False
    return True

def rotate_word(word):
    new_word = ''
    random_series = []
    for letter in word:
        if avoid(letter):
            random_number = random.randint(1,26)
            new_letter = rotate_letter(letter, random_number)
            new_word += new_letter
            random_series.append(random_number)
        else:
            new_word += letter
    return (new_word, random_series)

def shift(words):
    index = 0
    encrypted_words = []
    random_shifts = []
    while index &lt; len(words):
        (new_word, random_series) = rotate_word(words[index])
        encrypted_words.append(new_word)
        random_shifts.append(random_series)
        index += 1
    return (encrypted_words, random_shifts)

def get_input():
    words = []
    while True:
        user = (input('Input words to encrypt. Done when ready \n' ))
        if user == 'Done' or user == 'done':
            return words
        words.append(user)

def start():
    words = get_input()
    encrypted_words = []
    random_shifts = []
    (encrypted_words, random_shifts) = shift(words)
    print('UNENCRYPTED WORDS', words)
    print('ENCRYPTED WORDS', encrypted_words)
    print('RANDOM SHIFTS', random_shifts)

start()
Your function avoid is quite unnecessary and also confusing. I would expect avoid('@') to return True. When you store the letters as a global constant and as set, you should gain some speed (both because it does not have to be reconstructed again and again and because membership testing is very fast for a set. I replaced your function with letter not in letters_avoid.

Whenever you are doing string addition in Python, you are probably doing something wrong. This is because strings are immutable so for every addition a new string has to be created and the content of the strings to be added needs to be copied. This is quite slow. Rather, use a list to accumulate the letters of new_word and str.join them at the end.

When returning a tuple, the () are implicit. You can just write return a, b.

When iterating over words, you should just directly iterate over it, instead of its indices.

Using str.lower cuts down the double comparison if user == 'Done' or user == 'done' to if user.lower() == 'done':.

You should call your code with a if __name__ == '__main__': guard to allow importing your functions from another script without executing the whole script.

Final code:

import random


letters_avoid = set(' ,!?¿,*+-%/@1234567890')


def rotate_letter(letter, number):
    if letter == ' ':
        return letter
    if letter.islower():
        start = ord('a')
    elif not letter.islower():
        start = ord('A')

    normal_shift = ord(letter) - start
    new_letter = start + (normal_shift + number) % 26
    return chr(new_letter)


def rotate_word(word):
    new_word = []
    random_series = []
    for letter in word:
        if letter not in letters_avoid:
            random_number = random.randint(1, 26)
            new_word.append(rotate_letter(letter, random_number))
            random_series.append(random_number)
        else:
            new_word.append(letter)
    return "".join(new_word), random_series


def shift(words):
    encrypted_words = []
    random_shifts = []
    for word in words:
        new_word, random_series = rotate_word(word)
        encrypted_words.append(new_word)
        random_shifts.append(random_series)
    return encrypted_words, random_shifts


def get_input():
    words = []
    while True:
        user = input('Input words to encrypt. Done when ready \n')
        if user.lower() == 'done':
            return words
        words.append(user)


def start():
    words = get_input()
    encrypted_words, random_shifts = shift(words)
    print('UNENCRYPTED WORDS', words)
    print('ENCRYPTED WORDS', encrypted_words)
    print('RANDOM SHIFTS', random_shifts)

if __name__ == "__main__":
    start()