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