If Python strings are immutable, how can we change them with the replace() method?

Asked

Viewed 229 times

6

If Python strings are immutable, how can we change them with the method replace(), for example?

Ex:

s = "banana"

s = s.replace("b", "z")

print(s) # zanana

Is this a change of the original string or not? I’m updating the value of s and not creating a new string. That’s not it?

  • 7

    Behold id(s) before after the replace (In Cpython the id returns the memory address of your object - if you change, it is because you have created a new object).

  • 7

    Spoiler: if string was changeable, you wouldn’t need to reassign s in s = s.replace, just execute s.replace that would internally change the characters, which is not what happens in practice. Lists, for example, are mutable and you can do the append without having to do something like lista = lista.append(...)

  • 1

    s is a variable and not a object string. With the right concepts it’s easy to understand.

  • @Maniero disagree, the motto of the python language is that everything is an object. Maybe what you said is true for some languages but not for Python. When you interact with the object s is making a call to the method replace of class str.

  • 3

    I’ve seen some people say that, but I haven’t seen a canonical document with that. Already blog of influential people in the community saying this and the rest repeating as parrot, which does not mean that is correct. I have not yet read the answer given in full because I need to leave, but the beginning seems promising, not least because hkotsubo usually responds properly and does not buy these myths that people spread around. Be alert for those who want to learn right. Any language that wants to repel a concept of computing does not deserve credit. I’m not saying that language does that.

  • Maniero, the variable s references a string object. So much so that we can use methods accessing the variable, as in s.upper(). So I didn’t understand the differentiation of variable and object in this case.

  • Carlos, initially the variable s points to an object (the string "banana"). After the s = s.replace(...), the variable points to another object (the string "zanana"). That is, the variable only points to an object, but it is not the object: see in my answer that after the replace the variable s points to "zanana", but the string "banana" continues to exist - that is, we have a variable (s) and two objects (the strings "banana" and "zanana"). The variable, at the end, is just a name given to reference a value (read the links that Maniero indicated and that I also put in the answer)

  • The fact that you can call a method of an object through the variable does not mean that the variable is the object. What happens is that the method is called in the object to which the variable points, but it is still separate things. For example, x = 'abc', the variable x points to the string "abc". Then I do x = 'z'. The variable x changed (now points to "z"), but the string "abc" (the object for which x pointed before) remains unchanged. If the variable was the object itself (if x was the string "abc", instead of pointing to it), when doing x = 'z' the string "abc" would also be modified

Show 3 more comments

2 answers

8


This is an alteration of the original string or not?

Not.

Let’s follow step by step (in a simplified way) what happens:

s = "banana"

Here we create an object of the string type (to be more precise, an instance of the class str), whose content is the text "banana", and we assign in the variable s. I mean, we have something like this:

s aponta para "banana"

Then on this line:

s = s.replace("b", "z")

Here are two things happening: first replace returns another string (in the case, "zanana"), and then the result is assigned to the variable s. So now it’s like this:

s aponta para "zanana"

That is, the variable s now point to the string "zanana". But the string "banana" is still there, unchanged (it just doesn’t have anyone pointing to it anymore, but note that it itself hasn’t been modified).


One way to see that the variable has changed (which it points to another object) is to print the id of the same. According to documentation, in Cpython (which is probably the implementation you are using) the address of the object in memory is returned. So we can do this test:

s = "banana"
# imprime a string e o id
print(s, id(s)) # banana 19192864

s = s.replace("b", "z")
print(s, id(s)) # zanana 24317376

The exact numbers will change each time you run and will not be the same as the ones I put up. But the important thing is that the id before and after the replace will not be the same, because after the replace the variable s starts pointing to the other string that was generated, and no longer to the original.

This holds true for all methods that "modify" the string (such as upper, lower, etc): actually they return another string with the modified value. So much so that you can do so:

s = "banana"
# atribuir o resultado de replace para outra variável, em vez de "s"
outra = s.replace("b", "z")

print(s) # banana
print(outra) # zanana

If a string was changeable, the method replace would change the very value of s, but see above that was not the case. s remained "banana", and the variable outra, which receives the return of replace is pointing to the new string. The detail is that this variable could be itself s (as in your code), but this does not mean that the string has changed, but rather that the variable has started pointing to another string. Variable is one thing, and object is another.

If a string was changeable, the code below would not give error:

s = "banana"
# mudar o primeiro caractere da string (**dá erro!**)
s[0] = 'z' # TypeError: 'str' object does not support item assignment

For comparison purposes only, lists in Python are mutable, which means you can modify them without having to create another list or assign the other variable:

lista = [3, 20, 1]
print(id(lista))

# adiciona um elemento
lista.append(10)

# remove o primeiro
lista.pop(0)

# ordena
lista.sort()

print(lista) # [1, 10, 20]
print(id(lista)) # será o mesmo impresso no início

I printed out the id from the list before and after the operations, and see that it will be the same, as it remains the same list: only its elements are changed.

Notice I don’t need to do something like lista = lista.sort(), for sort already sorts the list itself (instead of returning another ordered list). Same does not happen with strings, so just call replace is not enough: you need to take the returned value because the original string is not modified. That’s what we mean by "strings are immutable".

  • Thanks for the full explanation hkotsubo. Since you mentioned the change of attribution to the variable s, when you change the value of the same variable, it is the same as when you use a method in a string, that is, the variable remains the same, but the Ids are different because they point to different values? For example: in x = 10 print(id(x)) x = 20 print(id(x)) the Ids will be different as they point to different objects. Is this case the same for using methods like replace, upper or Lower? It’s what you call the dynamically typed part of Python?

  • @Carlossanches When you call a method, it is called in the object to which the variable points. s.replace will call the replace for the string that s is pointing. I don’t know if that’s what you wanted to know...

  • @Carlossanches Already on "dynamically typed", this is something else (meaning that variables can change the type, for example, I can do s = 'abc' - s is a string - and then I can do s = 1 - s is an integer). That is, the same variable can assume values of completely different types. On this subject, I suggest reading here: https://answall.com/q/21508/112052

  • Okay. Thank you again.

5

According to class documentation str:

Python text data is treated as objects str, or strings. Strings are strings immutable of code points Unicode.

So, since strings are immutable in Python, it is to be expected that methods like the replace do not change the original string, but yes create and return a new string.

In the case of str.replace, it is possible to refer to the documentation to better understand what is happening:

str.replace(old, new)

Returns a copy string with all substring occurrences old replaced by new.

And indeed, as one of the comments on the question, can use the built-in function id to confirm that replace returns an object other than the original, since they have memory addresses[1] different:

>>> s = "banana"
>>> id(s)
4441918384
>>> s = s.replace("b", "z")
>>> id(s)
4440837616 # ID diferente do anterior. Trata-se, portanto, de um novo valor!

Some languages allow modifying strings, but since this is not the case for Python, it is not correct to expect a method (or any other code) to be able to modify a string, since the language itself determines that it is a immutable data structure.

So in your code,

s = "banana"
s = s.replace("b", "z")

print(s) # zanana

You are not modifying the string created by the literal expression "banana". The replace is creating one new string that is ultimately used in reallocation of the variable s.

What changes is the variable s, since you are changing the value to which it points. Before, it pointed to "banana" and, after the reallocation, points to "zanana".

The code in question creates two different strings, which are never or will be modified.

For more details read What is a variable?.


[1]: Memory address is the value returned by id in the Cpython implementation. The documentation ensures, however, that it is a unique identification for different objects.

  • 2

    Thanks for the feedback Luiz. In fact, looking at the documentation answers my question clearly and quickly. " Copy" says it all. The doubt is generated in who is starting, like me, because the variable is the same. But the different Ids already respond to this too.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.