Python3 — индекс вне допустимого диапазона (Vigenere Cipher)

Я начинаю писать код для шифрования шифром Виженера. Во-первых, я хочу сделать ключ. Ключ нужно повторять снова и снова, пока он не совпадет с длиной сообщения, которое нужно зашифровать, поэтому я создал функцию, которая сделает это за меня:

def makekey(key, message):
    finalkey = []
    print(message) # to see if the key fits under the message correctly
    key = list(key)
    while len(finalkey) < len(message): # while the key is shorter than the message
        for i in range(0, len(key)): # iterate through characters in the key
            finalkey.append(key[i]) # append the characters in the processed key list
                if len(finalkey) > len(message): # if the final key is still bigger than the message
                    difference = len(finalkey) - len(message) # finds the difference between the two
                    del key[-difference] # removes the difference
    return ''.join(finalkey) # joins the final key into a string

print(makekey("LOVE", "Python")) # calling the function

Вывод должен выглядеть так:

Python
LOVELO

Но программа просто выдает ошибку индекса вне допустимого диапазона, и я понятия не имею, что происходит!

Сообщение об ошибке:

Traceback (most recent call last):
  File "test.py", line 14, in <module>
    print(makekey("LOVE", "Python")) # calling the function
  File "test.py", line 8, in makekey
    finalkey.append(key[i]) # append the characters in the processed key list
IndexError: list index out of range

person user3124306    schedule 03.02.2016    source источник
comment
есть более простой способ, использующий цикл   -  person Copperfield    schedule 03.02.2016
comment
itertools -> цикл.   -  person Charles Addis    schedule 16.09.2016


Ответы (1)


Причина, по которой ваш код не работает:

del key[-difference]

Должны были быть:

del finalkey[-difference]

Вы получаете IndexError при попытке доступа к key[3] в строке finalkey.append(key[i]) (где i=3) после того, как вы удалили этот элемент.

И просто для удовольствия, вот альтернативная реализация.

def make_key(key, message):
    """ Returns a string that repeats the `key` string until it's
        the length of the `message` string
    """
    if len(key) < len(message):  # only lengthen key if it's too short
        # In python, "abc" * 3 == "abcabcabc"
        # so what would we need to multiply our `key` by to get
        # it to be as long as `message`, or longer?
        # A guaranteed answer is: floor(len(message) / len(key)) + 1
        multiplier = (len(message) // len(key)) + 1
        key = key * multiplier
    # now we have a `key` that is at least as long as `message`
    # so return a slice of the key where the end of the slice == len(message)
    return key[:len(message)]

print(makekey("LOVE", "Python"))

Отпечатки: LOVELO

Изменить - загадочное однострочное решение

Если вы хотите, чтобы все, кто читает ваш код, закатывали глаза, вы можете попробовать следующее:

from itertools import islice, cycle
key = "LOVE"
message = "Python"
finalkey = ''.join(islice(cycle(key), len(message)))

Функция cycle принимает объект iterable — в нашем случае строку key — и повторяет его в бесконечном цикле. Итак, если мы создадим cycle("LOVE"), он будет генерировать "L", "O", "V", "E", "L", "O", "V", "E", "L" ... навсегда.

Функция islice позволяет нам взять «срез» объекта итератора. В Python «срез» — это термин для [0:3] части выражения new = old[0:3] — мы «вырезали» подмножество оригинала. Поскольку мы не хотим, чтобы наша строка была бесконечно длинной — что было бы не очень полезно — мы хотим просто взять часть созданного нами cycle:

islice(cycle(key), len(message)

Это берет наш итератор — cycle(key) — и нарезает его, начиная с индекса 0 и заканчивая индексом len(message). Это вернет другой итератор — на этот раз не бесконечный. Содержимое итератора теперь такое: "L", "O", "V", "E", "L", "O".

Теперь нам просто нужно сшить это islice в целую строку:

''.join(islice...) == "LOVELO"

Просто чтобы дать вам еще один инструмент в вашем наборе инструментов!

person Monkpit    schedule 03.02.2016
comment
Подождите, как работает ваша версия кода, и знаете ли вы, что пошло не так с моим кодом? - person user3124306; 03.02.2016
comment
@ user3124306 Я закончил редактировать свой ответ, чтобы полностью ответить на ваш вопрос. Я также представил некоторые предлагаемые изменения к самому вопросу. - person Monkpit; 03.02.2016
comment
Благодарю вас! Блин, я чувствую себя таким глупым из-за того, что не понял, что я сделал - клянусь, я просмотрел код трижды... Кроме того, что вы подразумеваете под «этажом», когда вы говорите «пол (len (сообщение) / len (ключ)). ) + 1'? - person user3124306; 03.02.2016
comment
При делении двух чисел минимальное значение результата округляется до ближайшего НАИМЕНЬШЕГО целого числа (для округления в большую сторону используется максимальное значение). В Python3 для этого используется оператор // (целочисленное деление). Итак, я хочу посмотреть, сколько целых раз ключ будет вписываться в сообщение, и игнорировать десятичное число. Допустим, len(message) == 10 и len(key) == 3 - 10//3 == 3. Но на данный момент, если я сделаю key * 3, у меня будет только 9 символов, так что просто добавьте 1 к результату. (10//3) + 1 == 4 и len(key*4) > len(message) гарантировано True. - person Monkpit; 03.02.2016
comment
@user3124306 user3124306 Я добавил в свой ответ еще один раздел, в котором используется itertools, чтобы сделать все в одной строке — это менее читабельно и более неясно, но это хороший инструмент, о котором нужно знать. Надеюсь, это все поможет! - person Monkpit; 03.02.2016
comment
простите, но еще одно, потому что я хочу убедиться, что я понимаю это: что, если это было 10//7? 7 входит в 10 один раз, когда оно целое, поэтому, если это был ключ * 1, у вас было бы 7 символов, а добавление 1 приведет к 8, что было бы меньше, чем 10-символьное сообщение? - person user3124306; 03.02.2016
comment
Закрываем — делаем key * ((10//7)+1) — это key * 2 длиной 14. Мы добавляем 1 к значению, используемому для умножения строки, а не к длине самой строки. - person Monkpit; 03.02.2016
comment
Большое спасибо, лучший ответ и объяснения, которые я получил на stackoverflow! - person user3124306; 03.02.2016