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 -- http://mail.python.org/mailman/listinfo/python-list