{"id":"functions-global-variables","slug":"global-variables","title":"Parameters, not outside variables","text":"So far, everything a function used came in through its parameters. But a function can also see the variables of the main program, the ones made outside every function. These are called **global variables**. Python lets a function use them, but it's a habit to avoid: it leads to bugs that hide well.\n\nHere is one. This program first worked with a single temperature, kept in the variable `temperature`. Then the conversion from Celsius to Fahrenheit was moved into a function, but one name inside it was left as it was:\n\n```python\ntemperature = 25\n\n\ndef to_fahrenheit(celsius):\n    fahrenheit = temperature * 9 / 5 + 32\n    print(f\"{celsius} C is {fahrenheit} F\")\n\n\nto_fahrenheit(0)\nto_fahrenheit(100)\nto_fahrenheit(25)\n```\n\n```output\n0 C is 77.0 F\n100 C is 77.0 F\n25 C is 77.0 F\n```\n\nWater doesn't freeze at 77 degrees Fahrenheit, and it doesn't boil there either. The parameter `celsius` only makes it into the text: the calculation still uses `temperature`, the global variable, so every call converts 25 degrees. The last line is correct, but only by luck, because its argument happens to be the value of the global variable. Had the function been tried only with 25, it would have seemed to work.\n\n### Why it goes wrong\n\nA function that uses only its parameters does the same work wherever it's called from: everything it needs comes in through the call. A function that reads a global variable depends on something outside it. When that variable changes, or doesn't exist, the function behaves differently, though nobody changed the function. Here is the same function in a program without the global variable:\n\n```python\ndef to_fahrenheit(celsius):\n    fahrenheit = temperature * 9 / 5 + 32\n    print(f\"{celsius} C is {fahrenheit} F\")\n\n\nto_fahrenheit(100)\n```\n\n```crash\nTraceback (most recent call last):\n  File \"main.py\", line 6, in <module>\n    to_fahrenheit(100)\n    ~~~~~~~~~~~~~^^^^^\n  File \"main.py\", line 2, in to_fahrenheit\n    fahrenheit = temperature * 9 / 5 + 32\n                 ^^^^^^^^^^^\nNameError: name 'temperature' is not defined\n```\n\nThis traceback has two parts: first the call on line 6, then line 2 inside `to_fahrenheit`, where the error happened. Read it from the bottom: the last lines show where the program stopped.\n\nThe checks on this site work like that second program. They load your program and call your function on its own, with their own arguments. If your function relies on a global variable, that variable may not exist when the checks call it, or it may hold a value that has nothing to do with their arguments.\n\n### Everything through parameters\n\nSo when a function needs a value, make it a parameter, and give the value as an argument in the call. Variables that the function makes for itself, such as `total` here, are fine: they belong to the function.\n\n```python\ndef price_with_tax(price, tax_percent):\n    total = price + price * tax_percent // 100\n    print(f\"{price} with {tax_percent}% tax is {total}\")\n\n\nprice_with_tax(100, 24)\nprice_with_tax(50, 10)\n```\n\n```output\n100 with 24% tax is 124\n50 with 10% tax is 55\n```\n","task":"This function was tried in a program that happened to have a global variable `count`, so it seemed to work. Called on its own, it fails. Fix it so it uses only its parameters. A call `order_summary(\"soup\", 2)` should print:\n\n```\nOrder: 2 x soup\nKitchen, please make 2 portions.\n```\n","kind":"code","seed":"def order_summary(dish, amount):\n    print(f\"Order: {amount} x {dish}\")\n    print(f\"Kitchen, please make {count} portions.\")\n","solution":"def order_summary(dish, amount):\n    print(f\"Order: {amount} x {dish}\")\n    print(f\"Kitchen, please make {amount} portions.\")\n","hints":["Check it and read the error. Which name is not defined, and on which line?","`count` isn't a parameter of the function. The number of portions is the parameter `amount`."],"tests":[{"name":"order_summary(\"soup\", 2)","stdin":"","call":"order_summary(\"soup\", 2)\n","expect":"Order: 2 x soup\nKitchen, please make 2 portions.","hidden":false},{"name":"order_summary(\"pancakes\", 5)","stdin":"","call":"order_summary(\"pancakes\", 5)\n","expect":"Order: 5 x pancakes\nKitchen, please make 5 portions.","hidden":true}],"require":[],"forbid":[],"more":[{"task":"What does this program print? Type each line it prints.\n","kind":"predict","seed":"size = 3\n\n\ndef show(number):\n    print(number, size)\n\n\ndef show_size(size):\n    print(size)\n\n\nshow(10)\nsize = 5\nshow(10)\nshow_size(7)\n","solution":"size = 3\n\n\ndef show(number):\n    print(number, size)\n\n\ndef show_size(size):\n    print(size)\n\n\nshow(10)\nsize = 5\nshow(10)\nshow_size(7)\n","hints":["`show` reads the global `size`, so it prints whatever `size` is at the moment of the call.","`show_size` has a parameter called `size`. Inside it, `size` is the argument of the call, not the global variable."],"tests":[{"name":"Output","stdin":"","expect":"10 3\n10 5\n7","hidden":false}],"require":[],"forbid":[],"lines":["10 3","10 5","7"]},{"task":"This function draws a box around a text, but it takes the text and the character from global variables. Change it so it takes them as parameters instead, in this order: `box(text, character)`. A call `box(\"Hi\", \"#\")` prints:\n\n```\n######\n# Hi #\n######\n```\n","kind":"code","seed":"def box():\n    print(character * (len(text) + 4))\n    print(f\"{character} {text} {character}\")\n    print(character * (len(text) + 4))\n","solution":"def box(text, character):\n    print(character * (len(text) + 4))\n    print(f\"{character} {text} {character}\")\n    print(character * (len(text) + 4))\n","hints":["The body can stay as it is. What's missing is in the header.","Name the two parameters in the parentheses of the header, separated by a comma: `def box(text, character):`."],"tests":[{"name":"box(\"Hi\", \"#\")","stdin":"","call":"box(\"Hi\", \"#\")\n","expect":"######\n# Hi #\n######","hidden":false},{"name":"box(\"Python\", \"*\")","stdin":"","call":"box(\"Python\", \"*\")\n","expect":"**********\n* Python *\n**********","hidden":true},{"name":"box(\"\", \"+\")","stdin":"","call":"box(\"\", \"+\")\n","expect":"++++\n+  +\n++++","hidden":true}],"require":[],"forbid":[]}]}