Your code defines a function with a clear input and a clear output. Also, you've put the call to it behind the if __name__ == '__main__':
. You've taken good habits on this points. Nonetheless, a few things can still be improved.
Python version
If you are learning Python, there is no good reason to start with Python 2 which is reaching its end of life. You should focus on learning Python 3, going to be supported for a while and with various warts corrected (and obviously more features).
In any case, the sets
module is not required since Python 2.6 since we have the set
builtin.
Style
There is an official standard Python style guide called PEP 8. This is highly recommended reading. It gives guidelines to help writing code that is both readable and consistent. The Python community tries to follow these guidelines, more or less strictly (a key aspect of PEP 8 is that it provides guidelines and not strict rules to follow blindly).
It deals with various aspects of the code style: naming conventions, indentation convention, etc.
You'll find various tools to try to check whether your code is PEP 8 compliant and if is it not, to try and fix this:
In your case, the major points I would fix are:
indentation should be 4 space
function names should snake_cased
you could try to take into account the advice to use join
instead of "CPython's efficient implementation of in-place string concatenation".
Naming
On top of the snake-case point mentioned above, a few things could be improved in the naming. Naming is a difficult part of programming so what I suggest are just personal, far from perfect, suggestions (from a non-native English speaker). In your case, I'd call the function tranform_string
so that it conveys the fact that:
The name str
is a bit of a problem because it hides the str
builtin. I do not find any alternative better than str_
which is still pretty poor :-(.
Edit: I don't know why I didn't think about "s" in the first place but after reading it in another answer, it looks like a much better option indeed. I am too lazy to edit my code everywhere.
Finally, i
is a good name for an (integer) index. For a single character, c
seems easier to understand (char
if you feel verbose).
Chained comparison
In Python, you can chain comparison. If your case, you can write:
elif 97 <= ord(cur_char) < 122:
More beautiful character check
From the Zen of Python:
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
In your case, you could avoid the call the ord
and get rid of the magic number by writing the simple:
elif 'a' <= cur_char < 'z':
Rewriting the logic
You could have the logic adding a character to new_str
in a single place.
cur_char = c.lower()
if cur_char == 'z':
new_char = 'A'
elif 'a' <= cur_char < 'z':
new_char = chr(ord(c) + 1)
if new_char in vowels:
new_char = new_char.upper()
else:
new_char = c
new_str += new_char
Then, you could try to makes the logic as close as possible to the way it is given in the problem for instance by making "z" a special case only for the operation where you take the following letter.
low_char = c.lower()
if 'a' <= low_char <= 'z':
new_char = 'a' if low_char == 'z' else chr(ord(c) + 1)
if new_char in vowels:
new_char = new_char.upper()
else:
new_char = c
new_str += new_char
Shorter way to define vowels
Because strings are iterable and set
takes any kind of iterable, you could write:
vowels = set('aeiou')
Better ascii check
You could reuse values from the string
module to make your test more beautiful:
if low_char in string.ascii_lowercase:
Removing string concatenation
As mentionned earlier, it is advised not to use string concatenation and use join
instead, when relevant.
At this stage, the code (with added tests) looks like:
import string
def transform_string(s):
vowels = set('aeiou')
lst = []
for c in s:
if c in string.ascii_letters:
new_c = 'a' if c.lower() == 'z' else chr(ord(c) + 1)
if new_c in vowels:
new_c = new_c.upper()
else:
new_c = c
lst.append(new_c)
return "".join(lst)
if __name__=='__main__':
assert transform_string("") == ""
assert transform_string("qwertyuiop") == "rxfsUzvjpq"
assert transform_string("asdfghjkl") == "btEghIklm"
assert transform_string("zxcvbnm") == "AydwcOn"
assert transform_string("1234567890") == "1234567890"
assert transform_string("QWERTYUIOP") == "RXFSUZVJPQ"
assert transform_string("ASDFGHJKL") == "BTEGHIKLM"
assert transform_string("ZXCVBNM") == "AYDWCON"
assert transform_string("(){}<>+_-=") == "(){}<>+_-="