{"id":"objects-methods","slug":"methods","title":"Methods belong to objects","text":"You have already called many functions that come after an object and a dot: `words.append(\"hi\")`, `line.split(\",\")`, `prices.values()`. These are **methods**: functions that belong to an object and work on that object. You write the object first, then a dot, then the method's name with its arguments in parentheses.\n\nCompare a method with an ordinary function. `len(motto)` is a function: the object goes inside the parentheses. `motto.count(\"s\")` calls a method of the string `motto`: it works on the string before the dot.\n\n```python\nmotto = \"Practice makes progress\"\n\nprint(len(motto))\nprint(motto.upper())\nprint(motto.count(\"s\"))\nprint(motto.find(\"makes\"))\nprint(motto.find(\"perfect\"))\n```\n\n```output\n23\nPRACTICE MAKES PROGRESS\n3\n9\n-1\n```\n\n`find` gives the index where the text starts, or `-1` when it isn't there at all. A method works on any string, a literal one too: `\"ready, steady, go\".count(\"y\")` is 2.\n\nEach type has its own methods: `upper` and `find` belong to strings, `append` and `sort` to lists, `values` and `items` to dictionaries. Calling a method that the object's type doesn't have stops the program:\n\n```python\nnumbers = [3, 1, 2]\nprint(numbers.upper())\n```\n\n```crash\nTraceback (most recent call last):\n  File \"main.py\", line 2, in <module>\n    print(numbers.upper())\n          ^^^^^^^^^^^^^\nAttributeError: 'list' object has no attribute 'upper'\n```\n\nAn `AttributeError` means the object has nothing by that name: a list has no `upper`.\n\n### Methods that return, methods that change\n\nStrings are immutable, so a string method never changes the string. It returns a new string, and the result is lost unless you store it:\n\n```python\ncode = \"abc-123\"\ncode.upper()\nprint(code)\ncode = code.upper()\nprint(code)\n```\n\n```output\nabc-123\nABC-123\n```\n\nLists are mutable, and many list methods change the list itself. Most of them return `None`, so don't assign their result:\n\n```python\nqueue = [\"Ann\", \"Bo\"]\nqueue.append(\"Cy\")\nfirst = queue.pop(0)\nprint(first)\nprint(queue)\n\nnumbers = [3, 1, 2]\nnumbers = numbers.sort()\nprint(numbers)\n```\n\n```output\nAnn\n['Bo', 'Cy']\nNone\n```\n\n`pop(0)` both changes the list and returns the item it took out. `sort` only changes the list and returns `None`, so the last assignment threw the sorted list away. Write just `numbers.sort()` on its own line.\n\nDictionary methods work the same way. `values()` gives the values of the dictionary it is called on:\n\n```python\nstock = {\"apples\": 4, \"pears\": 0, \"plums\": 7}\ntotal = 0\nfor amount in stock.values():\n    total += amount\nprint(total)\n```\n\n```output\n11\n```\n\n### A method on a method's result\n\nA method's result is an object too, so you can call a method on it straight away. Python works from left to right:\n\n```python\nanswer = \"  YES please \"\nprint(answer.strip().lower())\nprint(answer.lower().count(\"e\"))\n```\n\n```output\nyes please\n3\n```\n\n`answer.strip()` returns a new string without the spaces around it, and `.lower()` is called on that new string.\n","task":"Fill in the method names, so that the program first makes the sentence all small letters, then counts how many times `tea` appears in it and finds where the first `two` starts. It should print:\n\n```\n2\n8\n```\n","kind":"fill","seed":"sentence = \"Tea for two, and two for tea\"\nsmall = sentence.[[lower]]()\nprint(small.[[count]](\"tea\"))\nprint(small.[[find]](\"two\"))\n","solution":"sentence = \"Tea for two, and two for tea\"\nsmall = sentence.lower()\nprint(small.count(\"tea\"))\nprint(small.find(\"two\"))\n","hints":["The first method returns the string in small letters, the second counts how many times a piece of text appears, and the third gives the index where a piece of text starts.","The three methods are `lower`, `count` and `find`."],"tests":[{"name":"Output","stdin":"","expect":"2\n8","hidden":false}],"require":[],"forbid":[],"more":[{"task":"What does this program print? Type each line it prints.\n","kind":"predict","seed":"word = \"level\"\nword.upper()\nprint(word)\nletters = [\"b\", \"c\", \"a\"]\nletters.sort()\nprint(letters)\nprint(letters.pop())\nprint(word.upper().count(\"L\"))\n","solution":"word = \"level\"\nword.upper()\nprint(word)\nletters = [\"b\", \"c\", \"a\"]\nletters.sort()\nprint(letters)\nprint(letters.pop())\nprint(word.upper().count(\"L\"))\n","hints":["`word.upper()` on its own line returns a new string that isn't stored anywhere. Strings can't change, so `word` is still the same.","`sort` changes the list itself, and `pop()` with no index removes and returns the last item.","In the last line, `word.upper()` gives `\"LEVEL\"`, and `count` counts the capital L's in that."],"tests":[{"name":"Output","stdin":"","expect":"level\n['a', 'b', 'c']\nc\n2","hidden":false}],"require":[],"forbid":[],"lines":["level","['a', 'b', 'c']","c","2"]},{"task":"Tags typed by users are messy. Write a function `clean_tags(tags)` that takes a list of strings and changes **the list itself**, so that each tag has no spaces at its start or end and is in small letters. The function returns nothing.\n\nThis code:\n\n```python\nmy_tags = [\"  Python\", \"SQL \", \" Web Dev \"]\nclean_tags(my_tags)\nprint(my_tags)\n```\n\nprints\n\n```\n['python', 'sql', 'web dev']\n```\n","kind":"code","seed":"def clean_tags(tags):\n    pass\n\n\nif __name__ == \"__main__\":\n    my_tags = [\"  Python\", \"SQL \", \" Web Dev \"]\n    clean_tags(my_tags)\n    print(my_tags)\n","solution":"def clean_tags(tags):\n    for i in range(len(tags)):\n        tags[i] = tags[i].strip().lower()\n\n\nif __name__ == \"__main__\":\n    my_tags = [\"  Python\", \"SQL \", \" Web Dev \"]\n    clean_tags(my_tags)\n    print(my_tags)\n","hints":["`tag.strip().lower()` returns a cleaned copy of one tag, but strings never change, so the copy has to be put back into the list.","Go through the indexes with `for i in range(len(tags)):` and replace each item: `tags[i] = ...`.","A loop like `for tag in tags:` that changes only the variable `tag` doesn't change the list, and neither does building a new list. The caller's list itself must change."],"tests":[{"name":"Test 1","stdin":"","call":"my_tags = [\"  Python\", \"SQL \", \" Web Dev \"]\nclean_tags(my_tags)\nprint(my_tags)\n","expect":"['python', 'sql', 'web dev']","hidden":false},{"name":"Test 2","stdin":"","call":"tags = [\"Garden\", \" HERBS\", \"tomatoes  \", \"already clean\"]\nclean_tags(tags)\nprint(tags)\n","expect":"['garden', 'herbs', 'tomatoes', 'already clean']","hidden":true},{"name":"Test 3","stdin":"","call":"tags = []\nclean_tags(tags)\nprint(tags)\ntags.append(\"  Lonely  \")\nclean_tags(tags)\nprint(tags)\n","expect":"[]\n['lonely']","hidden":true}],"require":[],"forbid":[]}]}