{"id":"strings-and-lists-immutable","slug":"immutable","title":"Strings can't be changed","text":"Many of your tools work on lists and on strings alike: indexes, slices, `len`, `in` and `for`. So it seems natural to change a character of a string the way you change an item of a list. Try it:\n\n```python\nscores = [7, 4, 9]\nscores[1] = 8\nprint(scores)\nword = \"cat\"\nword[0] = \"b\"\n```\n\n```output\n[7, 8, 9]\n```\n\n```crash\nTraceback (most recent call last):\n  File \"main.py\", line 5, in <module>\n    word[0] = \"b\"\n    ~~~~^^^\nTypeError: 'str' object does not support item assignment\n```\n\nThe list takes the new item, but the string refuses. Once a string exists, nothing can change it: not a character, not its length, not its order. Strings are **immutable**. Even sorting works differently: the function `sorted` leaves the string alone and gives you a new list of its characters in order.\n\n### New strings instead\n\nWhat you can do is build a new string from pieces of the old one, and store it in the same variable:\n\n```python\nword = \"cat\"\nword = \"b\" + word[1:]\nprint(word)\nword = word + \"s\"\nprint(word)\nprint(sorted(\"cab\"))\n```\n\n```output\nbat\nbats\n['a', 'b', 'c']\n```\n\n`word[1:]` is `\"at\"`, so `\"b\" + word[1:]` is a new string, `\"bat\"`, and `word` now refers to it. Nothing happened to the string `\"cat\"` itself: `word` was simply given a different string.\n\nThat's why no string method changes the string it's called on. Methods like `upper` and `lower` return a new string instead, and the original stays as it was. To keep the result, store it, often in the same variable:\n\n```python\nname = \"ada\"\nname.upper()\nprint(name)\nname = name.upper()\nprint(name)\n```\n\n```output\nada\nADA\n```\n\nThe second line makes `\"ADA\"` and throws it away, because nothing stores it. A list method like `sort` is different: it changes the list itself and returns `None`. So for a list, `numbers.sort()` alone is right, and for a string, `name = name.upper()` is.\n","task":"The program should print the name the user types with its first letter in capitals, but it crashes. Fix it by building a new string instead of changing the old one:\n\n```\nName: grace\nGrace\n```\n","kind":"code","seed":"name = input(\"Name: \")\nname[0] = name[0].upper()\nprint(name)\n","solution":"name = input(\"Name: \")\nname = name[0].upper() + name[1:]\nprint(name)\n","hints":["A string can't be changed, so `name[0] = ...` is an error. Build a new string and store it in `name` instead.","The new string is the first letter in capitals followed by the rest of the name: `name[0].upper() + name[1:]`."],"tests":[{"name":"Test 1","stdin":"grace\n","expect":"Name: grace\nGrace","hidden":false},{"name":"Test 2","stdin":"linus\n","expect":"Name: linus\nLinus","hidden":true},{"name":"Test 3","stdin":"Ada\n","expect":"Name: Ada\nAda","hidden":true}],"require":[],"forbid":[],"more":[{"task":"Write the function `replace_at(text: str, index: int, char: str) -> str`, which returns a new string that is like `text` but has `char` at the given index. For example, `replace_at(\"table\", 0, \"c\")` returns `'cable'`. You can assume the index is in the string.\n","kind":"code","seed":"def replace_at(text: str, index: int, char: str) -> str:\n    pass\n\n\nif __name__ == \"__main__\":\n    print(replace_at(\"table\", 0, \"c\"))\n","solution":"def replace_at(text: str, index: int, char: str) -> str:\n    return text[:index] + char + text[index + 1:]\n\n\nif __name__ == \"__main__\":\n    print(replace_at(\"table\", 0, \"c\"))\n","hints":["The new string has three parts: everything before the index, the new character, and everything after the index.","Everything before the index is `text[:index]`, and everything after it is `text[index + 1:]`.","Join the three parts with `+` and return the result."],"tests":[{"name":"replace_at(\"table\", 0, \"c\")","stdin":"","call":"replace_at(\"table\", 0, \"c\")\n","expect":"'cable'","hidden":false,"returns":true},{"name":"replace_at(\"table\", 4, \"t\")","stdin":"","call":"replace_at(\"table\", 4, \"t\")\n","expect":"'tablt'","hidden":true,"returns":true},{"name":"replace_at(\"python\", 2, \"_\")","stdin":"","call":"replace_at(\"python\", 2, \"_\")\n","expect":"'py_hon'","hidden":true,"returns":true}],"require":[],"forbid":[]},{"task":"What does this program print? Type each line it prints.\n","kind":"predict","seed":"word = \"cold\"\nother = word\nword = word + \"er\"\nprint(word)\nprint(other)\nprint(sorted(\"tea\"))\nword.upper()\nprint(word)\n","solution":"word = \"cold\"\nother = word\nword = word + \"er\"\nprint(word)\nprint(other)\nprint(sorted(\"tea\"))\nword.upper()\nprint(word)\n","hints":["`word + \"er\"` makes a new string, and only `word` is given it. `other` still refers to the old string.","`sorted` on a string returns a list of its characters in order.","`word.upper()` makes a new string, but nothing stores it."],"tests":[{"name":"Output","stdin":"","expect":"colder\ncold\n['a', 'e', 't']\ncolder","hidden":false}],"require":[],"forbid":[],"lines":["colder","cold","['a', 'e', 't']","colder"]}]}