I tried this code to do simple string replacement:
X = "hello world"
X.replace("hello", "goodbye")
Why doesn't X change, from "hello world" to "goodbye world"?
I tried this code to do simple string replacement:
X = "hello world"
X.replace("hello", "goodbye")
Why doesn't X change, from "hello world" to "goodbye world"?
 
    
     
    
    This is because strings are immutable in Python.
Which means that X.replace("hello","goodbye") returns a copy of X with replacements made. Because of that you need replace this line:
X.replace("hello", "goodbye")
with this line:
X = X.replace("hello", "goodbye")
More broadly, this is true for all Python string methods that change a string's content "in-place", e.g. replace,strip,translate,lower/upper,join,...
You must assign their output to something if you want to use it and not throw it away, e.g.
X  = X.strip(' \t')
X2 = X.translate(...)
Y  = X.lower()
Z  = X.upper()
A  = X.join(':')
B  = X.capitalize()
C  = X.casefold()
and so on.
All string functions as lower, upper, strip are returning a string without modifying the original. If you try to modify a string, as you might think well it is an iterable, it will fail. 
x = 'hello'
x[0] = 'i' #'str' object does not support item assignment
There is a good reading about the importance of strings being immutable: Why are Python strings immutable? Best practices for using them
