{"assignment":{"_schema_version":2,"course_id":37,"date_created":"2025-08-17T17:32:06.361036+00:00","date_modified":"2025-09-09T21:50:05.872607+00:00","extra_instructor_files":"","extra_starting_files":"","forked_id":null,"forked_version":null,"hidden":false,"id":2468,"instructions":"## Names\n\n-   **Clear**: Understandable without explanation\n-   **Correct**: Accurately describes the thing it names\n-   **Concise**: As short as possible without losing meaning\n-   **Consistent**: Uses the same naming scheme throughout\n-   **Conventional**: Follows established naming conventions\n\nBack in the first unit, we talked about how important names are for code readability and maintainability. Choosing the right names can make a significant difference in how easily others (and your future self) can understand your code.\nFurther, if you do not know what to name a variable or function, then you probably do not know what it does.\nNames are the gateway to understanding, and heavily influence not only the reader of the code, but also the writer.\n\nPreviously, we established five criteria for good variable names.\nLet's review them again.\n\n## Clear\n\n```python check-if-valid-bad-names\ndef check_if_valid_guess(guess1: str, guess2: list[str]) -> bool:\n    \"\"\"\n    Check if the user's letter guess is valid.\n\n    Args:\n        guess1 (str): The first guess, should be a single letter.\n        guess2 (list[str]): The list of previous guesses.\n\n    Returns:\n        bool: True if the guess is valid, False otherwise.\n    \"\"\"\n    is_single_letter = not len(guess1) > 1\n    is_letter = guess1 in ascii_letters\n    is_not_repeated = guess2 not in guess1\n    return is_single_letter and is_letter and is_not_repeated\n```\n\nFirst, names should be clear: someone looking at the variable name should be able to understand its purpose without needing additional context. That means avoiding ambiguous names, obscure jargon, or names that otherwise are not self-explanatory.\nFor a properly documented function, the variable names should match the names used in the function's docstring.\nIf you find yourself writing a long description of a variable in the docstring, consider whether you can improve the variable name to make it more self-explanatory.\n\nIn the `check_if_valid_guess` function, we used the parameters `guess1` and `guess2`, which are very poor variable names indeed.\nIt suggests that the two variables are each individual guesses that the player has made, when the reality is that `guess1` is the current guess and `guess2` is the list of all previous guesses.\nThe fact that `guess2` is not plural even though it is a list is very suspicious and should set off alarm bells.\nIndeed, the poor variable names have made a specific error in the logic, which means they are not actually even correct!\n\n## Correct\n\n```python check-if-valid-good-names\ndef check_if_valid_guess(current_guess: str, past_guesses: list[str]) -> bool:\n    \"\"\"\n    Check if the user's letter guess is valid.\n\n    Args:\n        current_guess (str): The current guess, should be a single letter.\n        past_guesses (list[str]): The list of previous guesses.\n\n    Returns:\n        bool: True if the guess is valid, False otherwise.\n    \"\"\"\n    is_single_letter = not len(current_guess) > 1\n    is_letter = current_guess in ascii_letters\n    # Incorrect: past_guesses not in current_guess\n    is_not_repeated = current_guess not in past_guesses\n    return is_single_letter and is_letter and is_not_repeated\n```\n\nOur second criteria was that names should be correct: they should accurately describe the thing they name. This means avoiding misleading names, and being precise about the variable's purpose and usage.\nA common trap is that developers will choose a name, but then later change the implementation without updating the name, leading to confusion.\nIt can be a little extra work to keep all your variable names correct, but this is critical for avoiding certain kinds of errors.\n\nGoing back to our `check_if_valid_guess` function, the function checks if `guess2 not in guess1`, which is incorrect.\nIt should be `guess1 not in guess2`, checking if the guess is in the list of past guesses.\nHere we see a revised version of the function where the names have been changed to `current_guess` and `past_guesses`.\nThe expression for `is_not_repeated` becomes much more natural, and the original error obvious.\nBetter variable names helped us catch an error in our code!\n\n## Concise\n\n```python short-names\n@dataclass\nclass State:\n    sec: str       # secret\n    gs: list[str]  # guesses\n    wng: int       # wrong\n    lvl: int       # level\n    scr: int       # score\n```\n\nThe third criteria was that names should be concise, which means neither too short nor too long. A good rule of thumb is to aim for names that are descriptive enough to convey their meaning, but not so long that they become unwieldy. If you find yourself frustrated having to type a long name, consider a shorter one.\nOften, you can achieve conciseness by removing unnecessary words or by using abbreviations that are widely understood.\nSometimes, decomposing a function will make it easier to choose a concise name for the function's variables, because you can focus on the specific role each variable plays in the function.\n\nIn our experience, novices are more likely to make a name too short rather than too long.\nIn particular, some people like to remove vowels from names, resulting in names like `scr` for `score` and `lvl` for `level`.\nBack when variables were limited to 8 characters or less, this made a lot more sense.\nBut now, with modern IDEs and text editors, we have the ability to use longer, more descriptive names without worrying about character limits.\n\nTake a look at this alternative version of our `State` class, where all the variable names have been shortened. Although slightly easier to type, this version sacrifices clarity and readability.\nDoes `state.sec` mean `state.secret` or `state.secondary`?\nIs the `state.gs` variable referring to `state.guesses` or `state.guess_scores`?\nIs `state.wng` the number of wrong guesses or the number of winning guesses?\nThese are questions that the developer knows the answer to, but **others may not**.\nRemember, the goal is always to minimize confusion, because you will always be more confused reading code than you were when writing it (at least at first).\n\n## Consistent and Conventional\n\n```python\n# Inconsistent and unconventional names!\ndef RevealLetter(hidden: str, attempts: list[str], Q: int) -> str:\n    if hidden[Q] in attempts:\n        return hidden[Q]\n    return \"_\"\n\ndef reveal(secret: str, guesses: list[str]) -> str:\n    c0 = RevealLetter(secret, guesses, 0)\n    letter1 = RevealLetter(secret, guesses, 1)\n    c2 = RevealLetter(secret, guesses, 2)\n    c3 = RevealLetter(secret, guesses, 3)\n    return c0 + letter1 + c2 + c3\n```\n\nThe last two criteria are that names should be consistent and conventional.\nBeing consistent means that you should follow the same naming schema throughout the program, while being conventional means that you should try to follow the same naming schema as other programmers in your community.\nOften, programming languages have their own expectations for how to write variable names - in Python, we use underscores in variable and function names, but capital letters in dataclass names.\nBut this also applies to what words you use in the program.\nIf you were calling it a `secret_word` in one part of the program, you shouldn't change that variable to be `hidden_word` or `private_word` in another part. Consistency in naming helps avoid confusion and makes the code easier to read.\n\nIn the bad example shown here, we have our `reveal` and `reveal_letter` functions from before.\nBut now we have made changes to make their names inconsistent and unconventional.\nWe have changed `reveal_letter` to use capital camel case, which is not the standard convention in Python for function names.\nWe have also changed the name of the `index` variable to be a capital `Q`, which is not how most Python developers would name a variable being used as an index (they would be more likely to write `index` or `i`).\n\nThe changes also introduce inconsistency. First, the `secret` and `guesses` arguments are matched to the `hidden` and `attempts` parameters, even though nothing about them changed on the way.\nThis is likely to make readers wonder if the two sets of names refer to the same thing or not.\nSometimes, dropping or adding more context to matching argument-parameter pairs can add more clarity, but that is not happening here.\nFinally, the second variable in the body of `reveal` is named `letter1` instead of `c1`.\nA reader would quite correctly wonder if there was supposed to be something different about that variable, even though there isn't.\n\nThese changes make the code harder to read and understand, for no good reason.\nThey do not affect the correctness of the code, since they are only variable names.\nBut they could easily be hiding a mistake of some kind, and further errors are likely to be added down the line when someone needs to modify this code.\n\n## Magic Numbers and Constants\n\n```python naming-levels-bad\ndef choose_word(level: int) -> str:\n    levels = [\"heat\", \"dogs\", \"jazz\"]\n    return levels[level]\n\ndef is_game_over(level: int) -> bool:\n    return level > 4\n```\n\nSeparate from the question of how to name variables, is a question of when to use a variable.\nIn the `check_if_valid_guess` example from before, we introduced temporary variables to hold the result of each expression.\nThis allowed us to clarify what each part was doing and make the code easier to read.\nThis kind of change doesn't affect the correctness of the code, since they are only variable names.\nBut it might help us identify errors in our logic.\nA related idea is to extract out literal values into named constants, especially when they are used multiple times or when their meaning is not immediately clear.\nWe call these kinds of literal values \"magic numbers\" or \"magic strings\", since they appear in our source code without explanation even though they might be doing something important.\n\nBoth the `choose_word` and `is_game_over` functions refer to the concept of levels in the game, where each level has a different four letter word. When the current level is 0, the chosen word is \"heat\". When the current level is 1, the chosen word is \"dogs\", and so on.\nThis list of levels is clearly central to the game, and is indirectly referenced in the check for whether the game is over, when the code checks if the current level is greater than 4.\nUnfortunately, this is actually a mistake, because the game needs to be over when the current level is greater than the number of levels in that list.\nApparently, the `is_game_over` function was written or updated without reference to the actual list of levels defined in `choose_word`. Considering how spatially far apart these two functions are from each other in the codebase, that's not terribly surprising.\n\n<!-- Should I include advice on refactoring carefully? -->\n\n## Extracting Constants\n\n```python naming-levels-good\nLEVELS = [\"heat\", \"dogs\", \"jazz\"]\n\ndef choose_word(level: int) -> str:\n    return LEVELS[level]\n\ndef is_game_over(level: int) -> bool:\n    return level > len(LEVELS)\n```\n\nIn our enhanced version shown here, we have extracted the magic list literal `LEVELS` into a named constant. This makes it clear that this list is important and used in multiple places. It also makes it easier to update the list of levels in the future, since we only have to change it in one place. But most importantly, this allows us to make both `choose_word` and `is_game_over` correct by ensuring they both refer to the same source of truth for the list of levels.\nIt will be impossible for future editors of the code to get them out of sync with each other.\n\nOnce again, the day is saved by good variable names and constants.\n\n## Summary\n\n-   Use clear, concise, correct, consistent, and conventional variable names to make your code more readable.\n    -   Clear: Use names that describe the purpose of a variable without additional explanation.\n    -   Correct: Use names that accurately reflect the value or object they represent.\n    -   Concise: Use the shortest name that still conveys the meaning.\n    -   Consistent: Use the same naming conventions throughout your codebase to avoid confusion.\n    -   Conventional: Follow established naming conventions and patterns to make your code more predictable.\n-   Extract magic numbers and strings into named constants.\n","ip_ranges":"","name":"5B2) Names","on_change":"","on_eval":"","on_run":"","owner_id":1,"owner_id__email":"acbart@udel.edu","points":1,"public":true,"reviewed":false,"sample_submissions":[],"settings":"{\n  \"small_layout\": true,\n  \"header\": \"Names\"\n}","starting_code":"","subordinate":false,"tags":[],"type":"reading","url":"bakery_projects_names_read","version":3},"ip":"216.73.216.157","submission":{"_schema_version":3,"assignment_id":2468,"assignment_version":3,"attempts":0,"code":"","correct":false,"course_id":37,"date_created":"2026-05-20T14:01:48.493839+00:00","date_due":"","date_graded":"","date_locked":"","date_modified":"2026-05-20T14:01:48.493839+00:00","date_started":"","date_submitted":"","endpoint":"","extra_files":"","feedback":"","grading_status":"NotReady","id":2036917,"score":0.0,"submission_status":"Started","time_limit":"","url":"submission_url-ddb256f9-b26b-4284-9f78-24f1996fb88d","user_id":2044668,"user_id__email":"","version":0},"success":true}
