{ "cells": [ { "cell_type": "markdown", "id": "cba06bbar", "metadata": {}, "source": [ "\n", "**Other Ways to open this interlude:** \n", "[JupyterLite](https://python.porttack.com/jupyterlite-3db08322a4/notebooks/index.html?path=Chapter06b-Docstrings-and-Doctests.ipynb) | \n", "[Colab](https://colab.research.google.com/github/porttack/working-in-python/blob/v3/chapters/chap06b.ipynb) | \n", "[Read Only](https://python.porttack.com/chap06b.html?readonly) | \n", "Download\n", "\n", "" ] }, { "cell_type": "markdown", "id": "cba06bpane", "metadata": {}, "source": [ "\n", "

Ignore this cell — used when running JupyterLite.

\n", "\n", "\n", "
\n", "\n", "
\n", "\n", "" ] }, { "cell_type": "code", "execution_count": null, "id": "26d0884d", "metadata": { "tags": [] }, "outputs": [], "source": [ "from os.path import basename, exists\n", "\n", "def download(url):\n", " \"\"\"Download a file if it isn't already here, and return its filename.\"\"\"\n", " filename = basename(url)\n", " if not exists(filename):\n", " from urllib.request import urlretrieve\n", "\n", " local, _ = urlretrieve(url, filename)\n", " print(\"Downloaded \" + str(local))\n", " return filename\n", "\n", "download('https://github.com/porttack/working-in-python/raw/v3/working_in_python.py');\n", "\n", "import working_in_python\n", "# apcsp:begin type=\"note\" chapter=\"6b\"\n", "working_in_python.enable_docstring_reminders()\n", "# apcsp:end" ] }, { "cell_type": "markdown", "id": "31a122e9", "metadata": {}, "source": [ "# Docstrings and Doctests\n", "\n", "\n", "This interlude is not part of *Think Python*. It sits between Chapter 6 and Chapter 7\n", "because of what you just learned: functions that **return values**. Until now, your\n", "functions drew pictures or printed text, and the only way to check them was to look.\n", "Now they hand something back, and something that gets handed back can be checked\n", "automatically.\n", "\n", "\n", "In [Chapter 4](chap04.html) you wrote your first **docstring**: a short piece of text at\n", "the top of a function that says what the function does. A docstring is a *promise*. It\n", "tells whoever reads your code, including you three weeks from now, what they should\n", "expect if they call it.\n", "\n", "A promise that nobody checks is just a hope.\n", "\n", "In this interlude we'll add a second layer to that docstring: a few example calls, and the\n", "answer each one should give. Python can read those examples and run them. If the function\n", "keeps its promise, nothing happens. If it doesn't, Python tells you exactly which promise\n", "was broken.\n", "\n", "That second layer is called a **doctest**." ] }, { "cell_type": "markdown", "id": "b52fdbf3", "metadata": {}, "source": [ "## What a good docstring says\n", "\n", "Here is a function with no docstring at all." ] }, { "cell_type": "code", "execution_count": null, "id": "f578345b", "metadata": {}, "outputs": [], "source": [ "def price_with_tax(price, rate):\n", " return price * (1 + rate)" ] }, { "cell_type": "markdown", "id": "7a6ac496", "metadata": {}, "source": [ "It probably works. But answer these without scrolling: is `rate` a percentage like\n", "`8.5`, or a decimal like `0.085`? Does the result get rounded? What happens if `price`\n", "is negative?\n", "\n", "You can't answer any of those from the code alone, and neither can anyone else. A\n", "docstring answers them." ] }, { "cell_type": "code", "execution_count": null, "id": "adf764ee", "metadata": {}, "outputs": [], "source": [ "def price_with_tax(price, rate):\n", " \"\"\"Compute a price including sales tax.\n", "\n", " rate: tax rate as a decimal, so 8.5% is 0.085\n", "\n", " Returns the total as a float, not rounded.\n", " \"\"\"\n", " return price * (1 + rate)" ] }, { "cell_type": "markdown", "id": "d989363b", "metadata": {}, "source": [ "Three rules, and they're the whole game:\n", "\n", "1. A **summary line** that starts with a verb and says what the function does.\n", "2. **Document a parameter only when its name doesn't already say it.** `rate` needed\n", " explaining. `price` did not. A docstring that says \"price: the price\" wastes\n", " everyone's time.\n", "3. Say what comes **back**, if anything comes back.\n", "\n", "" ] }, { "cell_type": "markdown", "id": "2da34335", "metadata": {}, "source": [ "## Purpose, function, input, output\n", "\n", "There is a four-part frame worth learning now, because you will be asked to write it out\n", "in April and because a good docstring already contains three of the four parts.\n", "\n", "| Part | The question it answers | Where it lives in the docstring |\n", "|---|---|---|\n", "| **Purpose** | *Why* would anyone want this? | Nowhere. See below. |\n", "| **Function** | *What* does it do when it runs? | The summary line |\n", "| **Input** | What data does it receive? | The parameter notes |\n", "| **Output** | What does it produce? | The \"Returns...\" line |\n", "\n", "Look back at `price_with_tax`. \"Compute a price including sales tax\" is the\n", "**function**. The note about `rate` describes the\n", "**input**. The note about an unrounded float describes the\n", "output.\n", "\n", "The part a docstring never carries is **purpose**. \"Compute a price including sales tax\"\n", "tells you what the code does. It does not tell you that this is for a school store\n", "checkout, that the volunteers kept getting the tax wrong by hand, and that the whole\n", "program exists so the line moves faster. That's purpose, and it lives outside the\n", "function entirely.\n", "\n", "Keep the distinction. *Function* is behavior, described in the third person. *Purpose* is\n", "the need being served. Students routinely answer the purpose question with a feature\n", "list, and a feature list is neither one.\n", "\n", "\n", "\n", "> **A word about words.** This book says *function*. The AP exam says **procedure** for\n", "> the same thing, and the exam's written response asks about a \"student-developed\n", "> procedure\" by name.\n", ">\n", "> Historically those two words meant genuinely different things. In Pascal, a `procedure`\n", "> did some work and handed nothing back, while a `function` computed a value and returned\n", "> it. Fortran drew the same line between a `SUBROUTINE` and a `FUNCTION`. The distinction\n", "> was useful: the name told you whether a call was worth putting on the right-hand side of\n", "> an assignment.\n", ">\n", "> Modern languages mostly abandoned it. Python has only `def`, and a function that returns\n", "> nothing quietly returns `None`. The exam uses *procedure* for both cases, so the older\n", "> distinction is gone from its vocabulary too.\n", ">\n", "> Answer to both words. But it's worth knowing the difference existed, because you will\n", "> meet languages that still keep it, and because \"does this hand something back?\" remains\n", "> the first question worth asking about any function you're about to call." ] }, { "cell_type": "markdown", "id": "e4742598", "metadata": {}, "source": [ "## The docstring is data, not a comment\n", "\n", "A `#` comment is invisible to the running program. A docstring isn't. Python stores it\n", "on the function itself, which means the program can read its own documentation." ] }, { "cell_type": "code", "execution_count": null, "id": "f36441a6", "metadata": {}, "outputs": [], "source": [ "help(price_with_tax)" ] }, { "cell_type": "code", "execution_count": null, "id": "a9b6fa8e", "metadata": {}, "outputs": [], "source": [ "price_with_tax.__doc__" ] }, { "cell_type": "markdown", "id": "0d47cc45", "metadata": {}, "source": [ "This is why docstrings use triple quotes and go *inside* the function, on the first\n", "line. Move them anywhere else and they turn back into ordinary strings that nobody can\n", "find." ] }, { "cell_type": "markdown", "id": "37bf789f", "metadata": {}, "source": [ "## Your first doctest\n", "\n", "A doctest is an example call written inside the docstring, followed by the answer you\n", "expect. Each example starts with `>>>`, which is the prompt Python shows when you type\n", "at it directly." ] }, { "cell_type": "code", "execution_count": null, "id": "fe4fb7f5", "metadata": {}, "outputs": [], "source": [ "def double(x):\n", " \"\"\"Return twice the value of x.\n", "\n", " >>> double(5)\n", " 10\n", " >>> double(-3)\n", " -6\n", " \"\"\"\n", " return 2 * x" ] }, { "cell_type": "markdown", "id": "68345db0", "metadata": {}, "source": [ "Read those two lines as a sentence: *if someone calls `double(5)`, the answer should\n", "be `10`.* That's a claim about the function, written in a form Python can check.\n", "\n", "To check it, we need a helper. Import it from `working_in_python`:" ] }, { "cell_type": "code", "execution_count": null, "id": "46e509fa", "metadata": {}, "outputs": [], "source": [ "from doctest import run_docstring_examples\n", "\n", "def run_doctests(func):\n", " run_docstring_examples(func, globals(), name=func.__name__)\n", "\n", "run_doctests(double)" ] }, { "cell_type": "markdown", "id": "b8657990", "metadata": {}, "source": [ "Nothing happened. That's the point: **no news is good news.** `run_doctests` only\n", "speaks up when something is wrong.\n", "\n", "" ] }, { "cell_type": "markdown", "id": "368a1be0", "metadata": {}, "source": [ "> **Note for later.** `run_doctests` is a convenience that exists because we're\n", "> working in notebooks. When you start writing `.py` files, the same tests run a\n", "> different way. We'll get there." ] }, { "cell_type": "markdown", "id": "ee99a823", "metadata": {}, "source": [ "## When a test fails\n", "\n", "Here's the same function with a bug. The docstring still makes the same promise." ] }, { "cell_type": "code", "execution_count": null, "id": "a8cafa68", "metadata": {}, "outputs": [], "source": [ "def double_broken(x):\n", " \"\"\"Return twice the value of x.\n", "\n", " >>> double_broken(5)\n", " 10\n", " >>> double_broken(-3)\n", " -6\n", " \"\"\"\n", " return x + 2" ] }, { "cell_type": "code", "execution_count": null, "id": "e7720d63", "metadata": {}, "outputs": [], "source": [ "run_doctests(double_broken)" ] }, { "cell_type": "markdown", "id": "8ca02e06", "metadata": {}, "source": [ "Notice what the failure report gives you, in order:\n", "\n", "- **which call** failed\n", "- **Expected**, what the docstring promised\n", "- **Got**, what the code actually did\n", "\n", "And notice which test caught it. `double_broken(5)` returns `7`, so that one fails too --\n", "but if the only test had been `double_broken(2)`, the answer would have been `4`, the test\n", "would have passed, and the bug would have shipped.\n", "\n", "Choosing test cases is not busywork. It's the skill." ] }, { "cell_type": "markdown", "id": "9f71e256", "metadata": {}, "source": [ "## Choosing test cases\n", "\n", "Each of those `>>>` lines is a **test case**: one input, paired with the output it should\n", "produce. That pairing is the whole definition, and it's worth holding onto, because the\n", "exam asks about test cases in exactly those terms.\n", "\n", "A test case that always passes teaches you nothing. Aim for three kinds:\n", "\n", "| Kind | What it checks | Example for `is_even(n)` |\n", "|---|---|---|\n", "| **Typical** | The ordinary case anyone would try | `is_even(4)` |\n", "| **Boundary** | The value where behavior changes | `is_even(0)` |\n", "| **Edge** | The unusual input you suspect is wrong | `is_even(-3)` |\n", "\n", "Typical cases are the ones students write. **Boundary** and edge\n", "cases are the ones they skip, and those are where the bugs live: zero, empty, one, the\n", "first, the last, the day the month rolls over." ] }, { "cell_type": "code", "execution_count": null, "id": "cb7d0059", "metadata": {}, "outputs": [], "source": [ "def can_ride(height_inches, age):\n", " \"\"\"Check whether someone may ride, given height and age.\n", "\n", " Riders must be at least 48 inches tall and at least 7 years old.\n", "\n", " >>> can_ride(52, 9)\n", " True\n", " >>> can_ride(48, 7)\n", " True\n", " >>> can_ride(72, 6)\n", " False\n", " \"\"\"\n", " return height_inches >= 48 and age >= 7" ] }, { "cell_type": "code", "execution_count": null, "id": "76942c20", "metadata": {}, "outputs": [], "source": [ "run_doctests(can_ride)" ] }, { "cell_type": "markdown", "id": "5135726b", "metadata": {}, "source": [ "The second test is the boundary: exactly 48 inches, exactly 7 years. If someone\n", "later \"fixes\" the code by changing `>=` to `>`, that test fails immediately.\n", "\n", "The third test is the interesting one. A very tall six-year-old is exactly the case where\n", "a careless writer would have used `or` instead of `and`. A test case can be funny and\n", "still be the most useful test in the file.\n", "\n", "" ] }, { "cell_type": "markdown", "id": "03a6ac9c", "metadata": {}, "source": [ "## Tests as documentation\n", "\n", "Read these two descriptions of the same function:\n", "\n", "> Returns the portion of the string before the first separator, or the whole string if\n", "> the separator does not occur.\n", "\n", "versus\n", "\n", "```\n", ">>> first_field('name,age,city', ',')\n", "'name'\n", ">>> first_field('solo', ',')\n", "'solo'\n", "```\n", "\n", "The prose is more complete. The examples are faster. Good documentation has both, and\n", "the examples are the part people actually read." ] }, { "cell_type": "markdown", "id": "4f66b538", "metadata": {}, "source": [ "## What doctests can't check\n", "\n", "Doctests compare what your function produces against text you wrote by hand. That works\n", "beautifully when the answer is predictable, and not at all when it isn't." ] }, { "cell_type": "code", "execution_count": null, "id": "d0b9d2c2", "metadata": {}, "outputs": [], "source": [ "def average(a, b):\n", " \"\"\"Return the mean of two numbers.\n", "\n", " >>> average(1, 2)\n", " 1.5\n", " >>> average(0.1, 0.2)\n", " 0.15000000000000002\n", " \"\"\"\n", " return (a + b) / 2" ] }, { "cell_type": "markdown", "id": "1f61ada1", "metadata": {}, "source": [ "That second expected value looks like a typo. It isn't. It's the actual answer, and you\n", "met this in Chapter 1: a computer stores a number in a fixed number of bits, and some\n", "decimal values don't fit exactly, so the answer lands a hair off. That's called\n", "**roundoff error**, and it's not a bug in your code. It's the\n", "machine telling the truth about what it can represent.\n", "\n", "We'll come back to *why* when we get to binary. For now the practical consequence is\n", "enough: doctests on floats are fragile.\n", "\n", "Three things doctests handle badly:\n", "\n", "- **Floating-point results**, for the reason above\n", "- **Anything random**, since the answer changes every run\n", "- **Anything that depends on the current date or time**\n", "\n", "None of these mean \"don't test.\" They mean the test has to be written differently. We'll\n", "see how in a later interlude on unit testing." ] }, { "cell_type": "markdown", "id": "76217879", "metadata": {}, "source": [ "## Debugging\n", "\n", "### What a doctest can and cannot catch\n", "\n", "You've met three kinds of error so far. They fail in three different ways, and a doctest\n", "is only useful against one of them.\n", "\n", "| Kind | What happens | Can a doctest catch it? |\n", "|---|---|---|\n", "| **Syntax error** | The code breaks Python's grammar and never runs at all | No. The cell fails before any test exists |\n", "| **Run-time error** | The code runs, then blows up partway with a traceback | Sort of. You get the traceback instead of a pass/fail |\n", "| **Semantic error** | The code runs happily and gives the wrong answer | **Yes. This is the whole point** |\n", "\n", "The first two announce themselves. A **semantic** error does\n", "not: nothing turns red, nothing complains, and the program looks like it worked. That\n", "silence is why they're the hardest to find, and it's exactly the silence a doctest breaks.\n", "\n", "> **A word about words.** Downey calls this a *semantic* error. The AP exam calls the same\n", "> thing a **logic error**. Both mean \"it ran, and it was wrong.\"\n", "\n", "There are other kinds of error you'll meet later, when we look at how numbers are actually\n", "stored: **roundoff error**, which you saw above, and **overflow error**, which happens\n", "when a value is too large for the bits available to hold it. Python hides overflow from\n", "you almost completely, so it needs its own demonstration. Both belong to a later interlude on\n", "binary, not to this one.\n", "\n", "\n", "\n", "### Which one is lying, the code or the test?\n", "\n", "When a doctest fails, one of two things is wrong: the **code**, or the **test**.\n", "Beginners assume it's always the code. It isn't.\n", "\n", "Before changing anything, do a **hand trace**: work through the call on paper, line by\n", "line, writing down what each variable holds as you go. No running the code, no guessing.\n", "The exam will ask you to do this with nothing but a pencil, so it's worth practicing while\n", "you still have a machine to check yourself against.\n", "\n", "Then compare your traced answer to both the Expected and the Got.\n", "\n", "- If your traced answer matches **Expected**, the code is wrong. Fix the function.\n", "- If your traced answer matches **Got**, the test is wrong. Fix the docstring.\n", "- If it matches **neither**, you don't yet understand the problem, and changing code at\n", " random will not help. Go back to the description.\n", "\n", "That third case is the common one, and it's the whole reason to trace before you type." ] }, { "cell_type": "markdown", "id": "bc0732d2", "metadata": {}, "source": [ "\n", "## Glossary\n", "\n", "**docstring:** A string at the beginning of a function that documents what the function\n", "does; unlike a comment, it is stored on the function and can be read by the program.\n", "*(Python's name for what the exam calls* program documentation.*)*\n", "\n", "**doctest:** An example call and its expected result, written inside a docstring, that can\n", "be run automatically to check the function. *(Python; not exam vocabulary.)*\n", "\n", "**testing:** Checking that a program behaves correctly by running it on chosen inputs and\n", "comparing what comes out against what should have come out.\n", "\n", "**test case:** A single input, paired with the result it should produce.\n", "\n", "**boundary case:** A test case at the value where a function's behavior changes, such as\n", "zero, an empty string, or the first or last item.\n", "\n", "**edge case:** A test case at an unusual or extreme input, where a function is most likely\n", "to be wrong.\n", "\n", "**expected value:** What a test says the answer should be, as opposed to what the code\n", "actually produced.\n", "\n", "**pass:** A test whose actual result matches its expected value.\n", "\n", "**fail:** A test whose actual result does not match its expected value.\n", "\n", "**hand tracing:** Working through code on paper line by line, writing down each variable's\n", "value, in order to find an error without running the program.\n", "\n", "**semantic error:** An error that lets the program run but produces a wrong result. *(The\n", "exam calls this a* logic error.*)*\n", "\n", "**roundoff error:** A loss of precision that happens because a fixed number of bits cannot\n", "represent some numbers exactly.\n", "\n", "**program purpose:** The need a program serves, or the problem it solves; *why* it exists.\n", "\n", "**program function:** What a program does when it runs, described as behavior.\n", "\n", "**program input:** Data a program receives while it is running.\n", "\n", "**program output:** What a program produces: displayed text, a returned value, a file, a\n", "sound, a movement.\n", "\n", "**procedure:** The exam's word for a named, reusable block of code, whether or not it\n", "returns a value. This book says *function*. In older languages the two words were\n", "distinct: a procedure returned nothing, a function returned a value.\n", "\n", "**regression:** A bug that reappears in code that used to work. Tests exist mainly to\n", "catch these. *(Professional vocabulary, not exam vocabulary.)*\n", "" ] }, { "cell_type": "markdown", "id": "f3ffa080", "metadata": {}, "source": [ "\n", "**See also:** the [full vocabulary glossary](https://python.porttack.com/alignment/ap-vocabulary-glossary.html) collects every term in this book, alphabetized, alongside the complete AP CSP exam vocabulary list.\n", "" ] }, { "cell_type": "markdown", "id": "1c0ebd8c", "metadata": {}, "source": [ "\n", "## Homework\n", "\n", "Do these in order. Exercises 1 through 5 are required." ] }, { "cell_type": "code", "execution_count": null, "id": "481f38ad", "metadata": {}, "outputs": [], "source": [ "# This cell tells Jupyter to provide detailed debugging information\n", "# when a runtime error occurs. Run it before working on the exercises.\n", "\n", "%xmode Verbose" ] }, { "cell_type": "markdown", "id": "6c79b2f0", "metadata": {}, "source": [ "### Exercise 1\n", "\n", "Below is `is_leap_year` from Chapter 5, correct but undocumented. Add a docstring with a\n", "summary line and **three** doctests: one typical year, one boundary case, and one case\n", "that would catch a wrong implementation. Then run them.\n", "\n", "*(A hint on the boundary: 2000 and 1900 are both interesting, for different reasons.)*" ] }, { "cell_type": "code", "execution_count": null, "id": "e5a0c099", "metadata": {}, "outputs": [], "source": [ "def is_leap_year(year):\n", " return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)\n", "\n", "\n", "run_doctests(is_leap_year)" ] }, { "cell_type": "markdown", "id": "fb3faceb", "metadata": {}, "source": [ "### Exercise 2 *(choose one: do Exercise 2 **or** Exercise 3, not both)*\n", "\n", "Write a function `can_ride(height_inches, age)` that returns `True` if a rider is at least\n", "48 inches tall **and** at least 7 years old. Write the docstring first, including three\n", "doctests, then write the body. One of your three should be a boundary case.\n", "\n", "Then, in the markdown cell below, state the function's **purpose** in one sentence.\n", "Say something the docstring does not say." ] }, { "cell_type": "code", "execution_count": null, "id": "b2755607", "metadata": {}, "outputs": [], "source": [ "# Your function here\n", "\n", "\n", "run_doctests(can_ride)" ] }, { "cell_type": "markdown", "id": "073a7046", "metadata": {}, "source": [ "*Purpose, one sentence:*" ] }, { "cell_type": "markdown", "id": "8f99c6ff", "metadata": {}, "source": [ "### Exercise 3 *(choose one: see Exercise 2)*\n", "\n", "Write a function `letter_grade(score)` that returns `'A'` for 90 and above, `'B'` for 80\n", "to 89, `'C'` for 70 to 79, `'D'` for 60 to 69, and `'F'` below 60. Write the docstring\n", "first, including three doctests, then write the body. One of your three should be a\n", "boundary case.\n", "\n", "Then, in the markdown cell below, state the function's **purpose** in one sentence.\n", "Say something the docstring does not say." ] }, { "cell_type": "code", "execution_count": null, "id": "23008d41", "metadata": {}, "outputs": [], "source": [ "# Your function here\n", "\n", "\n", "run_doctests(letter_grade)" ] }, { "cell_type": "markdown", "id": "4b5ffd38", "metadata": {}, "source": [ "*Purpose, one sentence:*" ] }, { "cell_type": "markdown", "id": "a627aafe", "metadata": {}, "source": [ "### Exercise 4\n", "\n", "The function below is broken. Its doctests are correct. Run them, read the failure report,\n", "and fix the **function** so all tests pass. Do not change the docstring.\n", "\n", "Before you fix it, hand trace `count_down_to_zero(3)` and name which kind of error this\n", "is: syntax, run-time, or semantic." ] }, { "cell_type": "code", "execution_count": null, "id": "18e74230", "metadata": {}, "outputs": [], "source": [ "def count_down_to_zero(n):\n", " \"\"\"Return a list of numbers from n down to 0.\n", "\n", " >>> count_down_to_zero(3)\n", " [3, 2, 1, 0]\n", " >>> count_down_to_zero(0)\n", " [0]\n", " \"\"\"\n", " result = []\n", " while n > 0:\n", " result.append(n)\n", " n = n - 1\n", " return result\n", "\n", "\n", "run_doctests(count_down_to_zero)" ] }, { "cell_type": "markdown", "id": "640db3fd", "metadata": {}, "source": [ "### Exercise 5\n", "\n", "Here a function and its doctest disagree. **Exactly one of them is wrong.**\n", "\n", "Run the test. Then, in the markdown cell below, say which one is wrong and how you know.\n", "Then fix that one, and only that one." ] }, { "cell_type": "code", "execution_count": null, "id": "2701f0ee", "metadata": {}, "outputs": [], "source": [ "def middle_character(word):\n", " \"\"\"Return the middle character of a word with an odd number of letters.\n", "\n", " >>> middle_character('cat')\n", " 'a'\n", " >>> middle_character('hello')\n", " 'e'\n", " \"\"\"\n", " return word[len(word) // 2]\n", "\n", "\n", "run_doctests(middle_character)" ] }, { "cell_type": "markdown", "id": "7d41da10", "metadata": {}, "source": [ "*Type your answer here: which is wrong, the code or the test, and how do you know?*" ] }, { "cell_type": "markdown", "id": "1e2b3d6a", "metadata": {}, "source": [ "### Exercise 6\n", "\n", "Reflection, three or four sentences.\n", "\n", "You wrote docstrings in Chapter 4 without any way to check them. Now you can check them.\n", "Did knowing the test would run change how you wrote the docstring? Was there a moment\n", "where writing the examples first made the function easier to write, or harder?" ] }, { "cell_type": "markdown", "id": "4d1816f6", "metadata": {}, "source": [ "*Type your answer here.*" ] }, { "cell_type": "markdown", "id": "7cc88ccb", "metadata": {}, "source": [ "### Write a test that lies *(extra credit)*\n", "\n", "Write a function that is **completely correct**, along with a doctest that **fails\n", "anyway**. Then explain in a sentence why it fails.\n", "\n", "There is more than one way to do this, and the chapter mentions three of them." ] }, { "cell_type": "code", "execution_count": null, "id": "77f99311", "metadata": {}, "outputs": [], "source": [ "# Your function here" ] }, { "cell_type": "markdown", "id": "27a9e539", "metadata": {}, "source": [ "*Type your explanation here.*" ] }, { "cell_type": "code", "execution_count": null, "id": "c30c6c1c", "metadata": {}, "outputs": [], "source": [ "from working_in_python import time_check\n", "\n", "chapter_minutes = 0\n", "extra_exercises_minutes = 0\n", "longest = 0\n", "\n", "time_check(chapter_minutes, extra_exercises_minutes, longest)" ] }, { "cell_type": "markdown", "id": "05d62882", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "id": "ch06b-copy-heading", "metadata": {}, "source": [ "\n", "### Finished? Copy your work\n", "\n", "This isn't part of the exercises above. It's a tool. Run the cell below to copy this notebook (including anything you've run) so you can paste it into a document.\n", "" ] }, { "cell_type": "code", "execution_count": null, "id": "ch06b-copy-button", "metadata": {}, "outputs": [], "source": [ "working_in_python.show_copy_notebook_button()" ] }, { "cell_type": "markdown", "id": "34f2363e", "metadata": {}, "source": [ "\n", "---\n", "\n", "**Working in Python** — modified by Eric Brown for a high school Computer Science Principles class. Source and modifications: [github.com/porttack/working-in-python](https://github.com/porttack/working-in-python)\n", "\n", "---\n", "\n", "\n", "[Think Python: 3rd Edition](https://allendowney.github.io/ThinkPython/index.html)\n", "\n", "Copyright 2024 [Allen B. Downey](https://allendowney.com)\n", "\n", "Code license: [MIT License](https://mit-license.org/)\n", "\n", "Text license: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)\n", "\n", "\n", "---\n", "\n", "## Standards alignment\n", "\n", "**AP CSP:** [1.3 Program Design and Development](https://python.porttack.com/alignment/apcsp-standards-reference.html#T-1.3), [1.4 Identifying and Correcting Errors](https://python.porttack.com/alignment/apcsp-standards-reference.html#T-1.4). Big Idea 1, 10-13% of the exam\n", "**California 9-12:** [9-12.AP.20](https://python.porttack.com/alignment/ca-cs-standards-reference.html#S-9-12.AP.20), [9-12.AP.22](https://python.porttack.com/alignment/ca-cs-standards-reference.html#S-9-12.AP.22)\n", "**CSTA 2026:** [HS-PRO-TR-19](https://python.porttack.com/alignment/csta2026-standards-reference.html#T-HS-PRO-TR-19)\n", "**CA CTE (ICT):** [C4.9](https://python.porttack.com/alignment/ca-ict-anchor-standards-reference.html#T-C4.9), [C5.4](https://python.porttack.com/alignment/ca-ict-anchor-standards-reference.html#T-C5.4)\n", "\n", "The exam asks you to choose inputs and the outputs they should produce, then use the\n", "results to find errors. A doctest is exactly that, written down. On the Create\n", "Performance Task you'll be asked to describe two calls to a procedure you wrote, what\n", "each one tests, and what each returns. That is the same work you're doing here, in the\n", "same order.\n", "\n", "This interlude also names the three error types the exam distinguishes (syntax, run-time,\n", "and semantic, which the exam calls *logic*), and introduces the purpose / function /\n", "input / output frame that the written-response section asks you to fill in. Overflow and\n", "roundoff errors are named here but explained in a later interlude on binary, where the reason\n", "for them actually lives.\n", "\n", "**Syntax note:** docstrings and `>>>` are Python, not exam pseudocode. The exam has no\n", "notation for documentation or testing. What transfers is the habit, not the punctuation.\n", "" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 5 }