-1

I'm new with python and I really cannot find a way to do this.

I want to change the letters of a string based on some criteria. Here is the code:

for c in text:
    ordChar=ord(c)
    if ordChar>=65 and ordChar<=90:
        ordChar=ordChar+13
        if ordChar>90:
            ordChar=ordChar-90+64
        c=chr(ordChar)
    else:
        if ordChar>=97 and ordChar<=122:
            ordChar=ordChar+13
            if ordChar>122:
                ordChar=ordChar-122+96
            c=chr(ordChar)
return text

The returned text value is the same as the parameter value. I thought variables were pointers, so editing c, it should edit text. What am I doing wrong?

Daniele Vitali
  • 3,848
  • 8
  • 48
  • 71
  • 6
    In Python, Strings are **immutable**. That means that you can not edit a single part of a string as you're attempting to here. You're merely setting `c` to a different value – scohe001 Aug 26 '13 at 17:36
  • 2
    Are you trying to implement rot13? Have a look at some nice/clean ways to do it instead: http://stackoverflow.com/q/3269686/298479 – ThiefMaster Aug 26 '13 at 17:37
  • 2
    @Josh: Even if they were mutable, the method used here wouldn't work. `a = [1,2,3]; for c in a:\n c = None` won't change `a`. (of course you need – Tim Pietzcker Aug 26 '13 at 17:37
  • @TimPietzcker +1, you're right, but even if he fixed that, the deeper issue is the immutability – scohe001 Aug 26 '13 at 17:44
  • As a side note, you can just write `if 65 <= ordChar <= 90:`. But it's a lot more readable, and harder to get wrong, if you write `if 'A' <= c <= 'Z':`. An off-by-one error is obvious when comparing to `'Z'`; it's not obvious against `90` unless everyone who reads your code has the ASCII chart memorized. (You may also want to use `ord('Z')` instead of `90`, etc., within the body of the `if` statement, for similar reasons.) – abarnert Aug 26 '13 at 18:30

3 Answers3

5

You're not "editing" c, you're assigning it to something else. One alternative is to build a new string instead of trying to mutate the old one (which is impossible), then instead of c=... you would have newstr += ....

arshajii
  • 127,459
  • 24
  • 238
  • 287
2

As I explained in my comment, Strings in Python are immutable, so you'll need to make a new string, you cannot simply edit the old one. Your code can become:

new_text = ""
for c in text:
    ordChar=ord(c)
    if ordChar>=65 and ordChar<=90:
        ordChar=ordChar+13
        if ordChar>90:
            ordChar=ordChar-90+64
        c=chr(ordChar)
    else:
        if ordChar>=97 and ordChar<=122:
            ordChar=ordChar+13
            if ordChar>122:
                ordChar=ordChar-122+96
            c=chr(ordChar)
    new_text += c
return new_text
scohe001
  • 15,110
  • 2
  • 31
  • 51
2

Either build a new string as in arshajii's and Josh's answers, or use map or a list comprehension to apply the conversion character-by-character. (Either way, you are making a new string rather than converting the original.)

def convert_char(c):
    code = ord(c)
    if 65 <= code <= 90:
        code += 13
        if code > 90:
            code -= 26
    elif 97 <= ord(c) <= 122:
        code += 13
        if code > 122:
            code -= 26
    return chr(code)

 print ''.join(map(convert_char, "Testing"))

There are several nicer ways of doing this such as:

def convert_char(c):
    if 'a' <= c <= 'z':
        return chr(97 + (ord(c) - 6) % 26)
    elif 'A' <= c <= 'Z':
        return chr(65 + ord(c) % 26)
    return c

or

from string import lowercase, uppercase
def convert_char(c):
    if c in lowercase:
        return lowercase[(lowercase.index(c) + 13) % 26]
    if c in uppercase:
        return uppercase[(uppercase.index(c) + 13) % 26]
    return c

(The link in the comments to an earlier answer has more.)

Stuart
  • 9,597
  • 1
  • 21
  • 30