-3
def encrypt_caesar(plaintext):
    s = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    d = dict(map(s,s[3:]+ s[:3]))
    return ''.join(map(lambda l: d.get(l,l), plaintext.lower()))

Tried to copy this code from Stanford lecture slide and run it but it gives 'str' object is not callable error, what am I doing wrong?

leftunknown
  • 19
  • 1
  • 1
  • 6

1 Answers1

0

I guess the author of the slide didn't actually test the code.

The code you produced is trying to use map() to call s as a function:

map(s,s[3:]+ s[:3])
#   ^ This must be a *callable object*, like a function

If you wanted to create a dictionary mapping ASCII letters to a letter 3 spots along in the alphabet, use the zip() function instead:

d = dict(zip(s, s[3:] + s[:3]))

zip() then pairs up each element in s with each element in s[3:] + s[:3], and those (letter, letter + 3) pairs are then passed to dict() to form key-value pairs:

>>> s = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
>>> dict(zip(s, s[3:] + s[:3]))
{'A': 'D', 'I': 'L', 'L': 'O', 'X': 'A', 'N': 'Q', 'D': 'G', 'S': 'V', 'B': 'E', 'V': 'Y', 'C': 'F', 'K': 'N', 'W': 'Z', 'R': 'U', 'O': 'R', 'T': 'W', 'P': 'S', 'F': 'I', 'J': 'M', 'M': 'P', 'E': 'H', 'Q': 'T', 'H': 'K', 'G': 'J', 'Z': 'C', 'U': 'X', 'Y': 'B'}

Next, your last line will completely fail to do any encryption, because your map only handles uppercase letters, but you lowercased your input. Either produce a lowercase map, or lowercase your input.

Lowercasing the map could look like this:

def encrypt_caesar(plaintext):
    s = "abcdefghijklmnopqrstuvwxyz"
    d = dict(zip(s, s[3:] + s[:3]))
    return ''.join(map(lambda l: d.get(l, l), plaintext.lower()))

or you could just use the string.ascii_lowercase constant:

from string import ascii_lowercase

def encrypt_caesar(plaintext):
    d = dict(zip(ascii_lowercase, ascii_lowercase[3:] + ascii_lowercase[:3]))
    return ''.join(map(lambda l: d.get(l,l), plaintext.lower()))

Using this method is rather slow, however. For blazingly-fast 'encryption', use the str.translate() method; the input map for that is best produced with str.maketrans:

from string import ascii_lowercase as alc

def encrypt_caesar(plaintext, shift=3):
    map = str.maketrans(alc, alc[shift:] + alc[:shift])
    return plaintext.lower().translate(map)

I added a shift parameter to define how much of an alphabet shift should be applied.

I'll leave handling both lowercase and uppercase letters as an exercise to the reader!

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343