Others have already addressed the main problem -- strings are immutable, so you can't just switch a single character while you're iterating. You could use a bytearray
instead, but...
FWIW, this is a good candidate for string.translate
:
>>> import string
>>> fromchr = ''.join(chr(x) for x in range(97, 110) + range(65, 78))
>>> tochr = ''.join(chr(x+13) for x in range(97, 110) + range(65, 78))
>>> fromchr += ''.join(chr(x) for x in range(110, 256) + range(78, 91))
>>> tochr += ''.join(chr(x-13) for x in range(110, 256) + range(78, 91))
>>> trans = string.maketrans(fromchr, tochr)
>>> 'Hello, lorem ipsum dolor sit amet'.translate(trans)
'Uryyb, yberz vcfhz qbybe fvg nzrg'
The great thing here is that creating the translation table is a 1-time cost. After the translation table is created, you can use it as many times as you want. Your translation will happen in ~O(n) time in optimized C code, so I would be surprised if you could get an implementation that is much faster (or simpler really).
This way even handily beats the builtin 'rot13'
codec:
def rot13a(s):
return s.encode('rot13')
import string
fromchr = ''.join([chr(x) for x in range(97, 110) + range(65, 78)])
tochr = ''.join([chr(x+13) for x in range(97, 110) + range(65, 78)])
fromchr += ''.join(chr(x) for x in range(110, 256) + range(78, 91))
tochr += ''.join(chr(x-13) for x in range(110, 256) + range(78, 91))
trans = string.maketrans(fromchr, tochr)
def rot13b(s):
return s.translate(trans)
import timeit
test_string = 'Hello, lorem ipsum dolor sit amet'
print rot13a(test_string) == rot13b(test_string)
print timeit.timeit("rot13a(test_string)", "from __main__ import test_string, rot13a")
print timeit.timeit("rot13b(test_string)", "from __main__ import test_string, rot13b")
(my results):
True
1.52055001259 # rot13a
0.21444106102 # rot13b
Note that this is python2.x code. In python3.x, you can't just add the ranges like that since range
no longer returns a list
object. But, hopefully the idea is clear enough...