rot13 in a more Pythonic style?

A

Andy Dingley

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' )
 
N

Neil Cerutti

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().

str.translate is what I'd do.

import string
rot13table = string.maketrans(
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
'nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM')

print 'Sybevk Tenohaqnr, Fcyhaqvt ihe guevtt'.translate(rot13table)
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.

How would it being a lambda help you?
 
M

Martin P. Hellwig

Andy said:
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:'This is a TEST'
 
A

Andy Dingley

str.translate is what I'd do.

That's what I hope to do too, but it might not be possible (for the
live, complex example). It looks as if I have to make a test, then
process the contents of the code differently depending. There might
well be a translation inside this, but I think I still have to have an
explicit 3-way switch in there.


How would it being a lambda help you?

I'm going to use it in a context where that would make for cleaner
code. There's not much in it though.

I still don't understand what a lambda is _for_ in Python. I know what
they are, I know what the alternatives are, but I still haven't found
an instance where it permits something novel to be done that couldn't
be done otherwise (if maybe not so neatly).
 
G

Gabriel Genellina

I still don't understand what a lambda is _for_ in Python. I know what
they are, I know what the alternatives are, but I still haven't found
an instance where it permits something novel to be done that couldn't
be done otherwise (if maybe not so neatly).

A lambda is a shorthand for a simple anonymous function. Any lambda can be
written as a function:

lambda args: expression

is the same as:

def __anonymous(args): return expression

(but the inverse is not true; lambda only allows a single expression in
the function body).

Except for easy event binding in some GUIs, deferred argument evaluation,
and maybe some other case, the're not used much anyway. Prior common usage
with map and filter can be replaced by list comprehensions (a lot more
clear, and perhaps as fast - any benchmark?)
 
D

Dennis Lee Bieber

I still don't understand what a lambda is _for_ in Python. I know what
they are, I know what the alternatives are, but I still haven't found
an instance where it permits something novel to be done that couldn't
be done otherwise (if maybe not so neatly).

The only useful spot I've seen for them has been in callbacks to
Tkinter widgets... Rather than defining (silly example, I know)

def do_stuff(i):
...

def Button_0(event):
do_stuff(0)

def Button_1(event):
do_stuff(1)
....
def Button_9(event):
do_stuff(9)


with each button widget having a callback of

... =Button_0

... =Button_1

... ...

... =Button_9

One can just have

def do_stuff(i):
...

... =(lambda x=0: do_stuff(x))

... =(lambda x=1: do_stuff(x))

... ...

... =(lambda x=9: do_stuff(x))



This latter arrangement can be easily generated programmatically:

for z in range(10):
... =(lambda x=z: do_stuff(x))

--
Wulfraed Dennis Lee Bieber KD6MOG
(e-mail address removed) (e-mail address removed)
HTTP://wlfraed.home.netcom.com/
(Bestiaria Support Staff: (e-mail address removed))
HTTP://www.bestiaria.com/
 
B

Beej

I still don't understand what a lambda is _for_ in Python.

Python supports functional programming to a certain extent, and
lambdas are part of this.

http://linuxgazette.net/109/pramode.html
I know what
they are, I know what the alternatives are, but I still haven't found
an instance where it permits something novel to be done that couldn't
be done otherwise (if maybe not so neatly).

Strictly speaking, you can live your whole life without using them.
There's always a procedural way of doing things, as well.

-Beej
 
R

Rob Wolfe

Andy Dingley said:
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.

It looks to me like a good place to use closure and dictionaries.
I would write it this way:

def rot(step):
import string
rot_char = lambda a,c,step=step: chr((((ord(c) - ord(a)) + step) % 26) + ord(a))
make_dict = lambda a,s: dict([(x, rot_char(a, x)) for x in s])
d = make_dict('a', string.ascii_lowercase)
d.update(make_dict('A', string.ascii_uppercase))
def f(s):
return "".join([d.get(c) or c for c in s])
return f
'Sybevk Tenohaqnr, Fcyhaqvt ihe guevtt'
 
P

Paul Rubin

Andy Dingley said:
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.

I'm having a hard time understanding what you're getting at. Why
don't you describe the actual problem instead of the rot13 analogy.
 
B

bearophileHUGS

Martin P. Hellwig
for me (personal) being Pythonic means that I should
separate the logic and variables, etc...

Well, for me me Pythonic means using built-in functionalities as much
as possible (like using encode("rot13") or translate), and to write
less code, (avoiding overgeneralizations from the start too). It means
other things too.

Bye,
bearophile
 
P

Paul Rubin

Andy Dingley said:
c_rot13 = lambdaf c : (((c, uc_rot13(c)) [c in
'ABCDEFGHIJKLMNOPQRSTUVWXYZ']), lc_rot13(c) )[c in
'abcdefghijklmnopqrstuvwxyz']

Oh, I see what you mean, you have separate upper and lower case maps
and you're asking how to select one in an expression. Pythonistas
seem to prefer using multiple statements:

def c_rot13(c):
if c in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ': return uc_rot13(c)
elif c in 'abcdefghijklmnopqrstuvwxyz': return lc_rot13(c)
return c

You could use the new ternary expression though:

c_rot13 = lambda c: \
uc_rot13(c) if c in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' else \
(lc_rot13(c) if c in 'abcdefghijklmnopqrstuvwxyz' else \
c)

if I have that right.
 
M

Martin P. Hellwig

Martin P. Hellwig

Well, for me me Pythonic means using built-in functionalities as much
as possible (like using encode("rot13") or translate), and to write
less code, (avoiding overgeneralizations from the start too). It means
other things too.

Bye,
bearophile
Yup, but I have a sever case of "NIH" especially if the question asked
is something more general then the example given :)
However you are very much right, I reimplemented rot13 and translate in
a dull way here :)
 
A

Andy Dingley

Why don't you describe the actual problem instead of the rot13 analogy.

I don't know what the actual problem is! I need to perform a complex
mapping between "old style" structured identifiers and "new style"
structured identifers. As the original specification was never thought
through or written down anywhere, I'm now having to try and reverse-
engineer from 5 years of collected inconsistent practice. So far I
have about four pages of BNF to describe things and I'm still not sure
what's accurate, what's inaccurate spec and what's merely an error in
practice. Hopefully there's a neat little structure underlying it all
and a few typos I can merely ignore, but probably it really is just an
inconsistent structure that needs a lot of explicit tests around the
corner-cases to make sense of.

rot13 isn't the issue here, and I already know how to use .translate()
What I'm after is a tutorial of my Python coding style for an example
that's quite similar to the rot13 case. Your previous posting was
very helpful here.
 
T

tomtheisen

A lambda is a shorthand for a simple anonymous function. Any lambda can be
written as a function:

lambda args: expression

is the same as:

def __anonymous(args): return expression

(but the inverse is not true; lambda only allows a single expression in
the function body).

Except for easy event binding in some GUIs, deferred argument evaluation,
and maybe some other case, the're not used much anyway. Prior common usage
with map and filter can be replaced by list comprehensions (a lot more
clear, and perhaps as fast - any benchmark?)

They are still useful for reduce(), which has no listcomp equivalent
that I know of.
 
D

Dennis Lee Bieber

I don't know what the actual problem is! I need to perform a complex
mapping between "old style" structured identifiers and "new style"
structured identifers. As the original specification was never thought
through or written down anywhere, I'm now having to try and reverse-
engineer from 5 years of collected inconsistent practice. So far I
have about four pages of BNF to describe things and I'm still not sure
what's accurate, what's inaccurate spec and what's merely an error in
practice. Hopefully there's a neat little structure underlying it all
and a few typos I can merely ignore, but probably it really is just an
inconsistent structure that needs a lot of explicit tests around the
corner-cases to make sense of.
Sounds more like a case for a parser/lexer wherein the emitted "code
tokens" are the "new style" identifiers...
--
Wulfraed Dennis Lee Bieber KD6MOG
(e-mail address removed) (e-mail address removed)
HTTP://wlfraed.home.netcom.com/
(Bestiaria Support Staff: (e-mail address removed))
HTTP://www.bestiaria.com/
 
A

Andy Dingley

Sounds more like a case for a parser/lexer wherein the emitted "code
tokens" are the "new style" identifiers...

8-( I'm trying not to think about that....

Fortunately I don't think it's _quite_ that bad.
 
S

Steven D'Aprano

Possibly not, but that description of the problem would likely have
gotten more applicable help than a debate on the merits of
reimplementing translate().


The Original Poster did say in his first post that he knew about translate
and encode and he wasn't looking for those solutions:

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.
[end quote]

Perhaps rot13 wasn't the best choice in the world, but how was Andy to
know people would answer his post without reading it in full?

Oh wait, this is Usenet...

*wink*
 
A

Andy Dingley


Thanks, that's a _really_ interesting link (Oh, I need to learn
Scheme!)

My code now looks like this, which I'm starting to feel much happier
about in a functional sense.


c_rot = lambda c, chars : (chr((ord(c) - ord(chars[0]) +
(len(chars) // 2)) % len(chars) + ord(chars[0])))

c_rot13 = lambda c : (((c, \
c_rot(c, string.ascii_uppercase)) [c in string.ascii_uppercase]),
\
c_rot(c, string.ascii_lowercase)) [c in string.ascii_lowercase]

rot13 = lambda s : string.join([ c_rot13(c) for c in s ],'')



I also considered this, but don't trust it as it relies on the two
sets being mutually disjoint between their inputs and outputs.

qc_rot = lambda c, chars : (c, c_rot(c, chars)) [c in chars]
c_rot13 = lambda c : (qc_rot( qc_rot(c, string.ascii_lowercase),
string.ascii_uppercase))
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Staff online

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,012
Latest member
RoxanneDzm

Latest Threads

Top