{"id":"scope-passing-data","slug":"passing-data","title":"Passing data between functions","text":"A program made of several functions needs to move data between them. The module on strings and lists showed one way: through parameters and return values. Now that you know `global`, there's a second way: global variables that all the functions read and change. The first way is almost always the better one.\n\nWith parameters and return values, each function is an independent whole. What it needs comes in as arguments, and what it produces goes out with `return`. You can read the function and understand it without the rest of the program, test it by calling it with different arguments, and use it in another program. When many functions change the same global variables, any of them can change the data at any time, and finding which one caused a wrong value means reading all of them.\n\nHere is a program in three functions. The main program keeps the data, the list `temperatures`, and hands it to each function that needs it:\n\n```python\ndef read_temperatures(days):\n    temperatures = []\n    for day in range(1, days + 1):\n        temperatures.append(int(input(f\"Day {day}: \")))\n    return temperatures\n\n\ndef warm_days(temperatures):\n    count = 0\n    for temperature in temperatures:\n        if temperature >= 20:\n            count += 1\n    return count\n\n\ndef report(temperatures):\n    print(\"Highest:\", max(temperatures))\n    print(\"Lowest:\", min(temperatures))\n    print(\"Warm days:\", warm_days(temperatures))\n\n\ntemperatures = read_temperatures(4)\nreport(temperatures)\n```\n\n```input\n18\n22\n25\n17\n```\n\n```output\nDay 1: 18\nDay 2: 22\nDay 3: 25\nDay 4: 17\nHighest: 25\nLowest: 17\nWarm days: 2\n```\n\n- `read_temperatures` creates the list and returns it. The main program stores it in its own variable.\n- `report` gets the list as an argument, and passes it on to `warm_days`, which returns a number.\n- None of the functions uses a global variable. The parameter `temperatures` in each function is a local name for the list the caller gave it.\n\n### A main function\n\nYou can go one step further and put the main program in a function too, usually called `main`. Then the program has no global variables at all, and the line under `if __name__ == \"__main__\":` only starts it:\n\n```python\ndef main():\n    temperatures = read_temperatures(4)\n    report(temperatures)\n\n\nif __name__ == \"__main__\":\n    main()\n```\n\nNow `temperatures` is a local variable of `main`. The only way other functions can get it is as an argument, which is exactly the point.\n\n### A list is passed, not copied\n\nOne thing to remember from the module on references: a list given as an argument is the same list, not a copy. If a function changes it with `append` or by assigning to an index, the caller sees the change. That's useful when changing the list is the function's job. When it isn't, a function should build a new list and return it.\n","task":"This program works, but its functions share the global list `words`. Change it so that `read_words()` creates the list and returns it, and `longest(words)` gets the list as a parameter and returns the longest word (the first of them, if several are as long). The main program prints the result. Don't use `global`. A run looks like this:\n\n```\nWord: tree\nWord: elephant\nWord: cat\nWord: end\nLongest: elephant\n```\n","kind":"code","seed":"words = []\n\n\ndef read_words():\n    global words\n    while True:\n        word = input(\"Word: \")\n        if word == \"end\":\n            break\n        words.append(word)\n\n\ndef longest():\n    global words\n    best = \"\"\n    for word in words:\n        if len(word) > len(best):\n            best = word\n    print(\"Longest:\", best)\n\n\nif __name__ == \"__main__\":\n    read_words()\n    longest()\n","solution":"def read_words():\n    words = []\n    while True:\n        word = input(\"Word: \")\n        if word == \"end\":\n            break\n        words.append(word)\n    return words\n\n\ndef longest(words):\n    best = \"\"\n    for word in words:\n        if len(word) > len(best):\n            best = word\n    return best\n\n\nif __name__ == \"__main__\":\n    words = read_words()\n    print(\"Longest:\", longest(words))\n","hints":["In `read_words`, create the empty list inside the function and `return` it at the end. Remove the global `words = []` and the `global` lines.","`longest` gets a parameter, `def longest(words):`, and returns `best` instead of printing it.","In the main block: `words = read_words()`, then print the value of `longest(words)`."],"tests":[{"name":"Test 1","stdin":"tree\nelephant\ncat\nend\n","expect":"Word: tree\nWord: elephant\nWord: cat\nWord: end\nLongest: elephant","hidden":false},{"name":"print(longest([\"sun\", \"planet\", \"moon\"]))","stdin":"","call":"print(longest([\"sun\", \"planet\", \"moon\"]))\n","expect":"planet","hidden":false},{"name":"longest([\"ab\", \"cd\", \"e\"])","stdin":"","call":"longest([\"ab\", \"cd\", \"e\"])\n","expect":"'ab'","hidden":true,"returns":true},{"name":"read_words()","stdin":"red\ngreen\nend\n","call":"read_words()\n","expect":"Word: red\nWord: green\nWord: end\n['red', 'green']","hidden":true,"returns":true},{"name":"Test 5","stdin":"a\nend\n","expect":"Word: a\nWord: end\nLongest: a","hidden":true}],"require":[],"forbid":[{"pattern":"\\bglobal\\b","message":"Pass the list as an argument and return values instead of using `global`."}],"more":[{"task":"What does this program print? Type each line it prints.\n","kind":"predict","seed":"def add_item(items, item):\n    items.append(item)\n    return len(items)\n\n\nbasket = [\"pen\"]\ncount = add_item(basket, \"ink\")\nprint(count)\nprint(basket)\n","solution":"def add_item(items, item):\n    items.append(item)\n    return len(items)\n\n\nbasket = [\"pen\"]\ncount = add_item(basket, \"ink\")\nprint(count)\nprint(basket)\n","hints":["`items` is a local name for the same list that `basket` refers to: the list isn't copied.","`append` changes that one list, so the main program sees `ink` in `basket` too. The function returns the new length."],"tests":[{"name":"Output","stdin":"","expect":"2\n['pen', 'ink']","hidden":false}],"require":[],"forbid":[],"lines":["2","['pen', 'ink']"]},{"task":"The file of a month's expenses has a category and an amount on each line (`category;amount`), and a category can appear on many lines. The starter code has the `main` function ready. Write the two functions it uses:\n\n- `read_expenses(filename)` returns a dictionary from each category to its total amount, in the order the categories first appear in the file;\n- `print_report(expenses)` gets such a dictionary and prints each category and its total, then the total of everything.\n\nWith this `may.csv`\n\n```text\nfood;120\nrent;800\nfood;125\nfun;60\n```\n\na run looks like this:\n\n```\nFile: may.csv\nfood: 245\nrent: 800\nfun: 60\nTotal: 1105\n```\n","kind":"code","seed":"def read_expenses(filename):\n    pass\n\n\ndef print_report(expenses):\n    pass\n\n\ndef main():\n    expenses = read_expenses(input(\"File: \"))\n    print_report(expenses)\n\n\nif __name__ == \"__main__\":\n    main()\n","solution":"def read_expenses(filename):\n    expenses = {}\n    with open(filename) as file:\n        for line in file:\n            parts = line.strip().split(\";\")\n            category = parts[0]\n            if category not in expenses:\n                expenses[category] = 0\n            expenses[category] += int(parts[1])\n    return expenses\n\n\ndef print_report(expenses):\n    total = 0\n    for category, amount in expenses.items():\n        print(f\"{category}: {amount}\")\n        total += amount\n    print(\"Total:\", total)\n\n\ndef main():\n    expenses = read_expenses(input(\"File: \"))\n    print_report(expenses)\n\n\nif __name__ == \"__main__\":\n    main()\n","hints":["`read_expenses` reads the file line by line into a dictionary. When a category comes up for the first time, start its total at 0, then add the amount with `int`.","`print_report` gets the dictionary as its parameter. It goes through `expenses.items()`, prints each pair and adds the amounts to a total.","Neither function needs a global variable. `main` passes the dictionary from one to the other."],"tests":[{"name":"Test 1","stdin":"may.csv\n","files":{"may.csv":"food;120\nrent;800\nfood;125\nfun;60\n"},"expect":"File: may.csv\nfood: 245\nrent: 800\nfun: 60\nTotal: 1105","hidden":false},{"name":"print(read_expenses(\"june.csv\"))","stdin":"","call":"print(read_expenses(\"june.csv\"))\n","files":{"june.csv":"travel;300\nfood;80\ntravel;45\n"},"expect":"{'travel': 345, 'food': 80}","hidden":true},{"name":"print_report({\"books\": 30, \"food\": 210})","stdin":"","call":"print_report({\"books\": 30, \"food\": 210})\n","expect":"books: 30\nfood: 210\nTotal: 240","hidden":true},{"name":"Test 4","stdin":"july.csv\n","files":{"july.csv":"gifts;55\n"},"expect":"File: july.csv\ngifts: 55\nTotal: 55","hidden":true}],"require":[],"forbid":[]}]}