Register all the Caesar cyphers as codecs. Codecs are actually quite easy to make – they consist of a function that takes an encoding name and returns either None
if it doesn't know that codec name or a CodecInfo
object if it does. In fact, rot_13
is in the Python library in Python 2, so you can use that as a model.
import codecs
def search_function(encoding_name):
try:
name, rot = encoding_name.split('-')
rot = int(rot, 10)
if name != 'rot': return None
except ValueError:
return None
rot = rot % 26
offsets = [(i - rot) % 26 for i in range(26)]
decoding_map = codecs.make_identity_dict(range(256))
decoding_map.update((i, 65 + j) for i, j in enumerate(offsets, 65))
decoding_map.update((i, 97 + j) for i, j in enumerate(offsets, 97))
encoding_map = codecs.make_encoding_map(decoding_map)
class Codec(codecs.Codec):
def encode(self,input,errors='strict'):
return codecs.charmap_encode(input,errors,encoding_map)
def decode(self,input,errors='strict'):
return codecs.charmap_decode(input,errors,decoding_map)
class IncrementalEncoder(codecs.IncrementalEncoder):
def encode(self, input, final=False):
return codecs.charmap_encode(input,self.errors,encoding_map)[0]
class IncrementalDecoder(codecs.IncrementalDecoder):
def decode(self, input, final=False):
return codecs.charmap_decode(input,self.errors,decoding_map)[0]
class StreamWriter(Codec,codecs.StreamWriter):
pass
class StreamReader(Codec,codecs.StreamReader):
pass
return codecs.CodecInfo(
name='rot-{}'.format(rot),
encode=Codec().encode,
decode=Codec().decode,
incrementalencoder=IncrementalEncoder,
incrementaldecoder=IncrementalDecoder,
streamwriter=StreamWriter,
streamreader=StreamReader,
)
codecs.register(search_function)
>>> for i in range(26): print letters.encode('rot-{}'.format(i))
...
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
bcdefghijklmnopqrstuvwxyzaBCDEFGHIJKLMNOPQRSTUVWXYZA
cdefghijklmnopqrstuvwxyzabCDEFGHIJKLMNOPQRSTUVWXYZAB
defghijklmnopqrstuvwxyzabcDEFGHIJKLMNOPQRSTUVWXYZABC
efghijklmnopqrstuvwxyzabcdEFGHIJKLMNOPQRSTUVWXYZABCD
fghijklmnopqrstuvwxyzabcdeFGHIJKLMNOPQRSTUVWXYZABCDE
ghijklmnopqrstuvwxyzabcdefGHIJKLMNOPQRSTUVWXYZABCDEF
hijklmnopqrstuvwxyzabcdefgHIJKLMNOPQRSTUVWXYZABCDEFG
ijklmnopqrstuvwxyzabcdefghIJKLMNOPQRSTUVWXYZABCDEFGH
jklmnopqrstuvwxyzabcdefghiJKLMNOPQRSTUVWXYZABCDEFGHI
klmnopqrstuvwxyzabcdefghijKLMNOPQRSTUVWXYZABCDEFGHIJ
lmnopqrstuvwxyzabcdefghijkLMNOPQRSTUVWXYZABCDEFGHIJK
mnopqrstuvwxyzabcdefghijklMNOPQRSTUVWXYZABCDEFGHIJKL
nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM
opqrstuvwxyzabcdefghijklmnOPQRSTUVWXYZABCDEFGHIJKLMN
pqrstuvwxyzabcdefghijklmnoPQRSTUVWXYZABCDEFGHIJKLMNO
qrstuvwxyzabcdefghijklmnopQRSTUVWXYZABCDEFGHIJKLMNOP
rstuvwxyzabcdefghijklmnopqRSTUVWXYZABCDEFGHIJKLMNOPQ
stuvwxyzabcdefghijklmnopqrSTUVWXYZABCDEFGHIJKLMNOPQR
tuvwxyzabcdefghijklmnopqrsTUVWXYZABCDEFGHIJKLMNOPQRS
uvwxyzabcdefghijklmnopqrstUVWXYZABCDEFGHIJKLMNOPQRST
vwxyzabcdefghijklmnopqrstuVWXYZABCDEFGHIJKLMNOPQRSTU
wxyzabcdefghijklmnopqrstuvWXYZABCDEFGHIJKLMNOPQRSTUV
xyzabcdefghijklmnopqrstuvwXYZABCDEFGHIJKLMNOPQRSTUVW
yzabcdefghijklmnopqrstuvwxYZABCDEFGHIJKLMNOPQRSTUVWX
zabcdefghijklmnopqrstuvwxyZABCDEFGHIJKLMNOPQRSTUVWXY
(det)==("c")
=>det == "c"
\$\endgroup\$<=ord('m')
, not<
. Otherwise, you mapm
to a backtick instead of toz
(which also means your cipher isn't reversible). Also, it's simpler to just use the%
operator to wrap aroundz
instead of trying to get the fiddly details right and introducing bugs like this one. \$\endgroup\$