{"id":"references-list-parameters","slug":"list-parameters","title":"Lists as parameters","text":"A function call works like `today = todo`: the parameter gets a reference to the list you give as the argument, not a copy of it. The function and the code that calls it share one list, so a function can change a list it's given:\n\n```python\ndef add_bonus(scores: list):\n    for i in range(len(scores)):\n        scores[i] += 5\n\n\npoints = [10, 20, 30]\nadd_bonus(points)\nprint(points)\n```\n\n```output\n[15, 25, 35]\n```\n\n`add_bonus` has no `return`. It does its job by changing the list, so you call it on a line of its own, for what it does, not for a value.\n\nThe other way is to leave the original alone and return a new list:\n\n```python\ndef with_bonus(scores: list) -> list:\n    result = []\n    for score in scores:\n        result.append(score + 5)\n    return result\n\n\npoints = [10, 20, 30]\nnew_points = with_bonus(points)\nprint(points)\nprint(new_points)\n```\n\n```output\n[10, 20, 30]\n[15, 25, 35]\n```\n\nBoth are fine. What matters is that you know, and say, which one a function does: does it change the list it's given, or return a new one?\n\n### Assigning to the parameter changes nothing outside\n\nHere is a common mistake. This function should cut the list it's given down to its first `count` names. It takes them with a slice and assigns the slice to the parameter:\n\n```python\ndef keep_first(names: list, count: int):\n    names = names[:count]\n\n\nqueue = [\"Ada\", \"Ben\", \"Cleo\", \"Dev\", \"Eli\"]\nkeep_first(queue, 3)\nprint(queue)\n```\n\n```output\n['Ada', 'Ben', 'Cleo', 'Dev', 'Eli']\n```\n\nA slice is a new list, and `names = names[:count]` points the parameter `names` at it. That's a new value for one variable inside the function, just like `today = [\"sleep\"]` in the first step of this module. The list that `queue` refers to is never touched, and the new list is lost when the function ends.\n\nTo change the caller's list, change the list itself: assign to its indexes, as `add_bonus` does, or replace its items with a **slice assignment**. A slice on the left side of `=` replaces those items with the items of the list on the right. There can be fewer or more of them, so the list can get shorter or longer:\n\n```pycon\n>>> letters = [\"a\", \"b\", \"c\", \"d\", \"e\"]\n>>> letters[1:4] = [\"X\"]\n>>> letters\n['a', 'X', 'e']\n>>> letters[:] = [\"p\", \"q\", \"r\", \"s\"]\n>>> letters\n['p', 'q', 'r', 's']\n```\n\n`[:]` is the whole list, so `letters[:] = ...` replaces every item. That's the fix for `keep_first`: its line becomes `names[:] = names[:count]`. Python works out the right side first, the slice with the first `count` names, and then puts its items into the caller's list in place of the old ones.\n","task":"Write a function `cap_scores(scores, limit)` that changes the list it's given: every score greater than `limit` becomes `limit`. The function returns nothing.\n\n```python\nscores = [45, 120, 80, 101]\ncap_scores(scores, 100)\nprint(scores)\n```\n\n```\n[45, 100, 80, 100]\n```\n","kind":"code","seed":"def cap_scores(scores: list, limit: int):\n    pass\n\n\nif __name__ == \"__main__\":\n    scores = [45, 120, 80, 101]\n    cap_scores(scores, 100)\n    print(scores)\n","solution":"def cap_scores(scores: list, limit: int):\n    for i in range(len(scores)):\n        if scores[i] > limit:\n            scores[i] = limit\n\n\nif __name__ == \"__main__\":\n    scores = [45, 120, 80, 101]\n    cap_scores(scores, 100)\n    print(scores)\n","hints":["To change an item of the list, you need its index, so go through the indexes with `for i in range(len(scores)):`.","If `scores[i]` is greater than `limit`, assign `scores[i] = limit`.","The function doesn't need a `return`. The caller's list changes, and the caller sees the change."],"tests":[{"name":"Test 1","stdin":"","call":"scores = [45, 120, 80, 101]\ncap_scores(scores, 100)\nprint(scores)\n","expect":"[45, 100, 80, 100]","hidden":false},{"name":"Test 2","stdin":"","call":"scores = [3, 9, 5]\ncap_scores(scores, 4)\nprint(scores)\n","expect":"[3, 4, 4]","hidden":true},{"name":"Test 3","stdin":"","call":"scores = [1, 2]\ncap_scores(scores, 10)\nprint(scores)\n","expect":"[1, 2]","hidden":true}],"require":[],"forbid":[],"more":[{"task":"`shout_all(words)` should change the list it's given, so that every word is in capital letters with an exclamation mark at the end. It builds the right list, but the caller's list doesn't change. Fix it.\n\n```python\nwords = [\"hey\", \"stop\"]\nshout_all(words)\nprint(words)\n```\n\n```\n['HEY!', 'STOP!']\n```\n","kind":"code","seed":"def shout_all(words: list):\n    loud = []\n    for word in words:\n        loud.append(word.upper() + \"!\")\n    words = loud\n\n\nif __name__ == \"__main__\":\n    words = [\"hey\", \"stop\"]\n    shout_all(words)\n    print(words)\n","solution":"def shout_all(words: list):\n    loud = []\n    for word in words:\n        loud.append(word.upper() + \"!\")\n    words[:] = loud\n\n\nif __name__ == \"__main__\":\n    words = [\"hey\", \"stop\"]\n    shout_all(words)\n    print(words)\n","hints":["`words = loud` only points the parameter at the new list. The caller's list stays as it was.","Change the caller's list itself. A slice assignment replaces all of its items at once.","Change the last line to `words[:] = loud`."],"tests":[{"name":"Test 1","stdin":"","call":"words = [\"hey\", \"stop\"]\nshout_all(words)\nprint(words)\n","expect":"['HEY!', 'STOP!']","hidden":false},{"name":"Test 2","stdin":"","call":"words = [\"run\", \"now\", \"please\"]\nshout_all(words)\nprint(words)\n","expect":"['RUN!', 'NOW!', 'PLEASE!']","hidden":true}],"require":[],"forbid":[]},{"task":"Write a function `remove_all(items, value)` that removes every copy of `value` from the list it's given. It changes the list and returns nothing. If `value` isn't in the list, the list stays as it is.\n\n```python\nitems = [1, 3, 1, 2, 1]\nremove_all(items, 1)\nprint(items)\n```\n\n```\n[3, 2]\n```\n","kind":"code","seed":"def remove_all(items: list, value):\n    pass\n\n\nif __name__ == \"__main__\":\n    items = [1, 3, 1, 2, 1]\n    remove_all(items, 1)\n    print(items)\n","solution":"def remove_all(items: list, value):\n    while value in items:\n        items.remove(value)\n\n\nif __name__ == \"__main__\":\n    items = [1, 3, 1, 2, 1]\n    remove_all(items, 1)\n    print(items)\n","hints":["`items.remove(value)` removes the first copy of `value` from the list, and changes the caller's list too.","Repeat it as long as `value` is still in the list, with a `while` loop and `in`.","Another way: build a new list without the value and put it into the caller's list with `items[:] = ...`."],"tests":[{"name":"Test 1","stdin":"","call":"items = [1, 3, 1, 2, 1]\nremove_all(items, 1)\nprint(items)\n","expect":"[3, 2]","hidden":false},{"name":"Test 2","stdin":"","call":"items = [\"a\", \"b\", \"a\"]\nremove_all(items, \"a\")\nprint(items)\n","expect":"['b']","hidden":true},{"name":"Test 3","stdin":"","call":"items = [4, 5, 6]\nremove_all(items, 7)\nprint(items)\n","expect":"[4, 5, 6]","hidden":true},{"name":"Test 4","stdin":"","call":"items = [2, 2, 2]\nremove_all(items, 2)\nprint(items)\n","expect":"[]","hidden":true}],"require":[],"forbid":[]}]}