{"id":"more-features-default-parameters","slug":"default-parameters","title":"Default parameter values","text":"A parameter can have a **default value**, written with `=` after its name in the `def` line. When a call leaves that argument out, the parameter gets the default:\n\n```python\ndef greet(name, greeting=\"Hello\"):\n    print(f\"{greeting}, {name}!\")\n\n\ngreet(\"Ada\")\ngreet(\"Linus\", \"Good morning\")\ngreet(\"Grace\", \"\")\n```\n\n```output\nHello, Ada!\nGood morning, Linus!\n, Grace!\n```\n\nThe last call shows that an empty string is still an argument: the default is used only when the argument is missing. You've relied on defaults all along: `print` puts a line break at the end unless you give `end=...`, and `range(5)` starts from 0 unless you give it a start.\n\n### Keyword arguments\n\nWith several defaults, a call can name the parameter it gives with a keyword argument, as you've done with `sep=` and `end=` in `print`, and keep the defaults of the others:\n\n```python\ndef line(length=20, character=\"-\"):\n    return character * length\n\n\nprint(line())\nprint(line(5))\nprint(line(8, \"=\"))\nprint(line(character=\"*\"))\n```\n\n```output\n--------------------\n-----\n========\n********************\n```\n\nParameters with a default come after those without one. The other way round, Python couldn't tell which argument belongs to which parameter, and it stops before running anything:\n\n```python\ndef greet(greeting=\"Hello\", name):\n    print(f\"{greeting}, {name}!\")\n```\n\n```crash\n  File \"main.py\", line 1\n    def greet(greeting=\"Hello\", name):\n                                ^^^^\nSyntaxError: parameter without a default follows parameter with a default\n```\n\n### A list as a default: a trap\n\nA default value is made once, when the `def` line runs, not on every call. For a number or a string that doesn't matter. But a list can change, and then every call that uses the default shares the same list:\n\n```python\ndef add_item(item, basket=[]):\n    basket.append(item)\n    return basket\n\n\nprint(add_item(\"apple\"))\nprint(add_item(\"pear\"))\n```\n\n```output\n['apple']\n['apple', 'pear']\n```\n\nThe second call got a basket that already had an apple in it. The usual fix is `None` as the default, and a new list made inside the function: `def add_item(item, basket=None):`, with `if basket is None:` and `basket = []` at the start of the body.\n","task":"Fill in the blank so that `times` is 2 when the call doesn't give it. The program should print:\n\n```\nhaha\nhohoho\n```\n","kind":"fill","seed":"def repeat(text, times[[=2‖= 2]]):\n    return text * times\n\n\nprint(repeat(\"ha\"))\nprint(repeat(\"ho\", 3))\n","solution":"def repeat(text, times=2):\n    return text * times\n\n\nprint(repeat(\"ha\"))\nprint(repeat(\"ho\", 3))\n","hints":["A default value is written in the `def` line, right after the parameter's name.","Type `=2` into the blank."],"tests":[{"name":"Output","stdin":"","expect":"haha\nhohoho","hidden":false}],"require":[],"forbid":[],"more":[{"task":"Write a function `format_price(amount, currency=\"EUR\")` that returns the amount with two decimals and the currency after it: `format_price(12.5)` returns `\"12.50 EUR\"`, and `format_price(3, \"USD\")` returns `\"3.00 USD\"`.\n","kind":"code","seed":"def format_price(amount, currency):\n    pass\n\n\nif __name__ == \"__main__\":\n    print(format_price(12.5))\n    print(format_price(3, \"USD\"))\n","solution":"def format_price(amount, currency=\"EUR\"):\n    return f\"{amount:.2f} {currency}\"\n\n\nif __name__ == \"__main__\":\n    print(format_price(12.5))\n    print(format_price(3, \"USD\"))\n","hints":["The starter code's `def` line has no default yet. Give `currency` the default `\"EUR\"`.","Two decimals come from `:.2f` in an f-string: `f\"{amount:.2f} {currency}\"`."],"tests":[{"name":"format_price(12.5)","stdin":"","call":"format_price(12.5)\n","expect":"'12.50 EUR'","hidden":false,"returns":true},{"name":"format_price(3, \"USD\")","stdin":"","call":"format_price(3, \"USD\")\n","expect":"'3.00 USD'","hidden":false,"returns":true},{"name":"format_price(0.1, \"GBP\")","stdin":"","call":"format_price(0.1, \"GBP\")\n","expect":"'0.10 GBP'","hidden":true,"returns":true},{"name":"format_price(1234.567)","stdin":"","call":"format_price(1234.567)\n","expect":"'1234.57 EUR'","hidden":true,"returns":true},{"name":"format_price(amount=7, currency=\"SEK\")","stdin":"","call":"format_price(amount=7, currency=\"SEK\")\n","expect":"'7.00 SEK'","hidden":true,"returns":true}],"require":[],"forbid":[]},{"task":"Write a function `draw_box(width=5, height=3, character=\"#\")` that prints a box of the given character, `height` lines of `width` characters each. Calls can leave out any of the arguments, or name them. `draw_box(4, character=\"+\")` prints:\n\n```\n++++\n++++\n++++\n```\n","kind":"code","seed":"def draw_box():\n    pass\n\n\nif __name__ == \"__main__\":\n    draw_box()\n    draw_box(4, character=\"+\")\n","solution":"def draw_box(width=5, height=3, character=\"#\"):\n    for i in range(height):\n        print(character * width)\n\n\nif __name__ == \"__main__\":\n    draw_box()\n    draw_box(4, character=\"+\")\n","hints":["Write all three parameters, each with its default, in the `def` line: `def draw_box(width=5, height=3, character=\"#\"):`.","Print `height` lines in a loop. Each line is `character * width`."],"tests":[{"name":"draw_box(4, character=\"+\")","stdin":"","call":"draw_box(4, character=\"+\")\n","expect":"++++\n++++\n++++","hidden":false},{"name":"draw_box()","stdin":"","call":"draw_box()\n","expect":"#####\n#####\n#####","hidden":false},{"name":"draw_box(2, 4)","stdin":"","call":"draw_box(2, 4)\n","expect":"##\n##\n##\n##","hidden":true},{"name":"draw_box(height=1, character=\"=\")","stdin":"","call":"draw_box(height=1, character=\"=\")\n","expect":"=====","hidden":true}],"require":[],"forbid":[]}]}