rot13 in a more Pythonic style?

Martin P. Hellwig mhellwig at xs4all.nl
Wed Feb 14 12:01:48 EST 2007


Andy Dingley wrote:
> I'm trying to write rot13, but to do it in a better and more Pythonic
> style than I'm currrently using. What would  you reckon to the
> following pretty ugly thing?   How would you improve it?  In
> particular, I don't like the way a three-way selection is done by
> nesting two binary selections. Also I dislike stating the same
> algorithm twice, but can't see how to parameterise them neatly.
> 
> Yes, I know of .encode() and .translate().
> No, I don't actually need rot13 itself, it's just a convenient
> substitute example for the real job-specific task.
> No, I don't have to do it with lambdas, but it would be nice if the
> final function was a lambda.
> 
> 
> #!/bin/python
> import string
> 
> lc_rot13 = lambda c : (chr((ord(c) - ord('a') + 13) % 26 + ord('a')))
> 
> uc_rot13 = lambda c : (chr((ord(c) - ord('A') + 13) % 26 + ord('A')))
> 
> c_rot13 = lambda c : (((c, uc_rot13(c)) [c in
> 'ABCDEFGHIJKLMNOPQRSTUVWXYZ']), lc_rot13(c) )[c in
> 'abcdefghijklmnopqrstuvwxyz']
> 
> rot13 = lambda s : string.join([ c_rot13(c) for c in s ],'')
> 
> 
> print rot13( 'Sybevk Tenohaqnr, Fcyhaqvt ihe guevtt' )
> 

Well first of all, for me (personal) being Pythonic means that I should 
separate the logic and variables, in this case there is the rotation 
mechanism and the variable with the amount it should rotate.
Then of course the letters case is something I consider as a state of 
the letter itself, the meaning of the letter doesn't change.
And being a sucker for dictionaries I use them a lot

So with that in mind I would write a class like this:
###
class Rot(object):
     def __init__(self,amount = 13):
         self.__alpha = 'abcdefghijklmnopqrstuvwxyz'
         self.__amount = amount
         self.__index_string = dict()
         self.__crypt_index_string = dict()
         self.__string_index = dict()
         self.__crypt_string_index = dict()
         self.__position = 0

         self.__create_dicts()


     def __cypher(self,number):
         alpha_len = len(self.__alpha)
         rotation_overflow = alpha_len - self.__amount
         new_number = None

         if number > rotation_overflow:
             new_number = number - self.__amount

         else:
             new_number = self.__position + self.__amount

         return(new_number)


     def __create_dicts(self):
         for letter in self.__alpha:
             self.__position += 1

             self.__index_string[self.__position] = letter
             self.__crypt_index_string[self.__cypher(self.__position)] = 
letter

             self.__string_index[letter] = self.__position
             self.__crypt_string_index[letter] = 
self.__cypher(self.__position)


     def encrypt(self,text):
         text_list = list()
         letter_capital = None

         for letter in text:
             letter_capital = letter.isupper()
             letter = letter.lower()

             if letter not in self.__alpha:
                 text_list.append(letter)

             else:
                 position_plain = self.__string_index[letter]
                 letter_crypt = self.__crypt_index_string[position_plain]

                 if letter_capital:
                     letter_crypt = letter_crypt.upper()

                 text_list.append(letter_crypt)

         return("".join(text_list))


     def decrypt(self,text):
         text_list = list()
         letter_capital = None

         for letter in text:
             letter_capital = letter.isupper()
             letter = letter.lower()

             if letter not in self.__alpha:
                 text_list.append(letter)

             else:
                 position_crypt = self.__crypt_string_index[letter]
                 letter_plain = self.__index_string[position_crypt]

                 if letter_capital:
                     letter_plain = letter_plain.upper()

                 text_list.append(letter_plain)

         return("".join(text_list))
###

Testing if it works:
 >>> rot13.decrypt(rot13.encrypt("This is a TEST"))
'This is a TEST'

-- 
mph




More information about the Python-list mailing list