''in-place' string modifications in Python

In Python, strings are immutable.

What is the standard idiom to walk through a string character-by-character and modify it?

The only methods I can think of are some genuinely stanky hacks related to joining against a result string.

--

In C:

for(int i = 0; i < strlen(s); i++)
{
   s[i] = F(s[i]);
}

This is super expressive and says exactly what I am doing. That is what I am looking for.



Solution 1:[1]

Don't use a string, use something mutable like bytearray:

#!/usr/bin/python

s = bytearray("my dog has fleas")
for n in xrange(len(s)):
    s[n] = chr(s[n]).upper()
print s

Results in:

MY DOG HAS FLEAS

Edit:

Since this is a bytearray, you aren't (necessarily) working with characters. You're working with bytes. So this works too:

s = bytearray("\x81\x82\x83")
for n in xrange(len(s)):
    s[n] = s[n] + 1
print repr(s)

gives:

bytearray(b'\x82\x83\x84')

If you want to modify characters in a Unicode string, you'd maybe want to work with memoryview, though that doesn't support Unicode directly.

Solution 2:[2]

The Python analog of your C:

for(int i = 0; i < strlen(s); i++)
{
   s[i] = F(s[i]);
}

would be:

s = "".join(F(c) for c in s)

which is also very expressive. It says exactly what is happening, but in a functional style rather than a procedural style.

Solution 3:[3]

you can use the UserString module:

 >>> import UserString
... s = UserString.MutableString('Python')
... print s
Python
>>> s[0] = 'c'
>>> print s
cython

Solution 4:[4]

I'd say the most Pythonic way is to use map():

s = map(func, s) # func has been applied to every character in s

This is the equivalent of writing:

s = "".join(func(c) for c in s)

Solution 5:[5]

The question first states that strings are immutable and then asks for a way to change them in place. This is kind of contradictory. Anyway, as this question pops up at the top of the list when you search for "python string in-place modification", I'm adding the answer for a real in place change.

Strings seem to be immutable when you look at the methods of the string class. But no language with an interface to C can really provide immutable data types. The only question is whether you have to write C code in order to achieve the desired modification.

Here python ctypes is your friend. As it supports getting pointers and includes C-like memory copy functions, a python string can be modified in place like this:

s = 16 * "."
print s
ctypes.memmove(ctypes.c_char_p(s), "Replacement", 11)
print s

Results in:

................
Replacement.....

(Of course, you can calculate the replacement string at runtime by applying a function F to every character of the original string. Different ways how to do this have been shown in the previous answers.)

Note that I do not in any way encourage doing this. However, I had to write a replacement for a class that was mapped from C++ to python and included a method:

int readData(char* data, int length)

(The caller is supposed to provide memory with length bytes and the method then writes the available data -- up to length -- into that memory, returning the number of bytes written.) While this is a perfectly sensible API in C/C++, it should not have been made available as method of a python class or at least the users of the API should be made aware that they may only pass mutable byte arrays as parameter.

As you might expect, "common usage" of the method is as shown in my example (create a string and pass it together with its length as arguments). As I did not really want to write a C/C++ extension I had to come up with a solution for implementing the behavior in my replacement class using python only.

Solution 6:[6]

string.translate is probably the closest function to what you're after.

Solution 7:[7]

Strings are iterable and can be walked through like lists. Strings also have a number of basic methods such as .replace() that might be what you're looking for. All string methods return a new string. So instead of modifying the string in place you can simply replace its existing value.

>>> mystring = 'robot drama'
>>> mystring = mystring.replace('r', 'g')
>>> mystring
'gobot dgama'

Solution 8:[8]

Assigning a particular character to a particular index in a string is not a particularly common operation, so if you find yourself needing to do it, think about whether there may be a better way to accomplish the task. But if you do need to, probably the most standard way would be to convert the string to a list, make your modifications, and then convert it back to a string.

s = 'abcdefgh'
l = list(s)
l[3] = 'r'
s2 = ''.join(l)

EDIT: As posted in bstpierre's answer, bytearray is probably even better for this task than list, as long as you're not working with Unicode strings.

s = 'abcdefgh'
b = bytearray(s)
b[3] = 'r'
s2 = str(b)

Solution 9:[9]

>>> mystring = "Th1s 1s my str1ng"
>>> mystring.replace("1", "i")
'This is my string'

If you want to store this new string you'll have to mystring = mystring.replace("1", "i"). This is because in Python strings are immutable.

Solution 10:[10]

If I ever need to do something like that I just convert it to a mutable list

For example... (though it would be easier to use sort (see second example) )

>>> s = "abcdfe"
>>> s = list(s)
>>> s[4] = "e"
>>> s[5] = "f"
>>> s = ''.join(s)
>>> print s
abcdef
>>>
# second example
>>> s.sort()
>>> s = ''.join(s)

Solution 11:[11]

Here is an example using translate to switch "-" with "." and uppercase "a"s

>>> from string import maketrans
>>> trans_table = maketrans(".-a","-.A")
>>> "foo-bar.".translate(trans_table)
'foo.bAr-'

This is much more efficient that flipping to byte array and back if you just need to do single char replacements

Solution 12:[12]

def modifyIdx(s, idx, newchar):
    return s[:idx] + newchar + s[idx+1:]

Solution 13:[13]

You can use StringIO class to receive file-like mutable interface of string.

Solution 14:[14]

I did that like this:

import tempfile
import shutil

...

f_old = open(input_file, 'r')
with tempfile.NamedTemporaryFile() as tmp:
    for line in f_old:
        tmp.write(line.replace(old_string, new_string))
    f_old.close()
    tmp.flush()
    os.fsync(tmp)
    shutil.copy2(tmp.name, input_file)
    tmp.close()

Solution 15:[15]

Here's my pythonic solution for In-place string reversal.

Accounts for white spaces too.

Note: It won't match any special characters if included in input_string except for underscore ( '_' )

i/p - "Hello World" => o/p - "olleH dlroW"

import re

def inplace_reversal(input_string):
    
    list_of_strings = re.findall(r'\s|(\w+)',input_string)
    
    output_string= ''
    
    for string in list_of_strings:
        
        if string == '':
            
            output_string += ' '
        
        else:
            
            output_string += string[::-1]
    
    return output_string

print(inplace_reversal('__Hello__ __World__         __Hello__       __World__ '))

>>> __olleH__ __dlroW__         __olleH__       __dlroW__