Style Guide - CP1404/Starter GitHub Wiki

This is the style guide for Python programming in IT@JCU, particularly the subjects CP1401 and CP1404 - Programming 1 and 2. It is based on Python's PEP8 standard

Identifier Naming

The main guideline for good identifier naming is that 100% of the identifiers in a program should be meaningful.
Variable names, function names, class names, constants...
Choosing good names is surprisingly important!
Poor names commonly lead to errors.
Good names save time reading and fixing code.
Good naming is also a good habit and attitude to get into for increased code quality.

Conventions for Python names

  • module_name or modulename depending on readability - use the _ to separate words especially if leaving it out is confusing
  • ClassName (known as PascalCase or CapWords or upper CamelCase)
  • function_name, variable_name (lowercase_with_underscores)
  • CONSTANT_NAME (all CAPS with underscores)

Following these conventions means any Python programmer reading your code knows when they see a name like THIS_THING it must be a constant, and ThisThing must be a class. If they see ThisThing = 3, that's just wrong... confusing... takes more work to read and see what's different/wrong.

Best-practice suggestions:

  • Use words from the problem domain. If the question/problem uses a term like "tax rate", then tax_rate is probably a good variable name. If the problem states that you need to determine a grade, then determine_grade would probably be a good choice for a function.
  • Don't be cute, don’t use abbreviations (What is atm?)
  • Avoid names that imply something that isn't true (including: don't reuse names for different things); avoid "disinformation" and inconsistency. E.g., if you use the word "get" to mean "get from input", e.g. age = get_age() then don't also use it to mean the thing you got, get_name = "Monty" or use the same verb to mean something else, like bmi = get_bmi(height, weight) (this should be more like bmi = calculate_bmi(height, weight) since we're not "getting").
  • Use pronounceable names (modymdhms?)
  • Avoid mental mapping (a = number, b = total). If an identifier name needs a comment to explain it, just improve the name.
  • Make meaningful distinctions (account, account_data, account_details, account_info - what's the difference?)
  • If you have similar concepts, you need to differentiate these. E.g., if you only had one kind of discount, then discount would be an OK variable name, but if you had two, you need to differentiate between the discount_rate and the discount_amount. Don't name one generally (e.g., tax) and the other specifically (e.g., tax_rate).
  • Use nouns for variables and verbs for functions. E.g., age = get_age() (noun = verb()). get_result = result() is confusing.
  • Use the phrase "This function will..." to help you name functions, e.g., "This function will determine_status()" is a valid sentence, but "This function will status()" is not.
  • Use the phrase "This variable stores..." to help you name variables, e.g., "This variable stores a name" is a valid sentence, but "This variable stores a get_name" is not.
  • Use plurals for collections like lists and avoid plurals for singular values. for child in children or for name in names works, but if we read names = "Monty" or name = ["Monty", "Python"], that's confusing.
  • number_of_ is a good common prefix for variables that represent a number of something. Don't use plurals, like people, to store singular values. The "number of people" variable would be better as number_of_people. It's unambiguous and no, it's not too long.
  • Boolean variables and functions should be named so conditions are readable and usually start with is_ or has_ or similar. E.g., if is_winner: is readable, but if status: is not.
  • A reasonable way to name dictionaries (aka maps) is the key_to_value pattern, e.g., name_to_phone or level_to_reward (using something like phones or levels here would imply they are lists/sets/tuples/etc. not dictionaries).
  • There are a number of commonly-used names that you should avoid using for anything else. E.g., i is usually an index for a list or similar. for i in range(n) makes sense; for i in children does not.
  • Never use built-in names like min, sum, print, etc. for your own names as this will override the existing names and prevent you from using them. It's also confusing to the reader. This also goes for module names - don't use os.py or random.py or other names that are existing Python modules.
  • Do not include a variable's type in its name (known as "systems Hungarian notation"). E.g., don't use name_list (just names) or str_name (just name).
  • Avoid ambiguous names, including check. What does "check" mean? To get something valid? To check an existing value is valid (then it should sound Boolean)? To keep getting something until it's valid? To...?

Our word choices

Let's decide on some standards for use within CP1401 and CP1404 (and hopefully other IT@JCU subjects).
These are not world or PEP8 standards, but they are our style. Follow these standards like you would the internal style guide of the company you work for as a programmer.

  • get - to get input from the user. get name (pseudocode), get_valid_number()...
  • calculate - to perform calculations. calculate_average(), calculate_bmi()...
  • determine - to determine something without calculating (usually with a series of if decisions). determine_result(), determine_penalty()...
  • number_of_x - to store an integer count of something. number_of_people, number_of_rows...

Commenting

As always, we follow the official Python styles for comments. Comments are to help humans read your code - these should be simple and helpful, written for someone about your own level of understanding. Don't include comments if they don't help.

Do

  • Add comments for anything that you think would likely be difficult to understand when you or someone else (of about your level) reads it. This is not a substitute for writing good clean code with meaningful variable names. If improving your names means you don't need comments, then do that!
    Consider the following examples and decide which is easier to understand:
# Calculate area of circle by multiplying radius (user_float) by 2 * PI
result = 2 * constant * user_float

# or

area = 2 * math.pi * radius

# or

area = calculate_circle_area(radius)
  • Write comments in the imperative voice (same as commit messages). E.g. # Calculate chance of rain not # Calculates chance of rain.

  • Remove old commented-out code when you're finished with it.

  • Put # inline comments above the code they refer to.
    End-of-line comments are only acceptable if they are short and should not extend off to the right too far - put them above the line if they do. Again, it's about readability. Horizontal scrolling is not good.

  • Write function comments at a general level, e.g. a function docstring that says how main will use the function is not appropriate. What if we want to use the function in a different way?

Don't

  • "This function will..." or similar is always redundant.

  • Don't add comments that are completely obvious. These are considered "noise" because they make your code harder to read. You're not writing a tutorial. E.g. # initialise variables, # import statements... say nothing useful. If your code has good names, it's probably already self-documenting and doesn't need commenting.
    Consider the following example. What does the comment add that's not already said by the code?... Nothing.

# Opens 'places.csv' file for reading
input_file = open('places.csv', 'r')
  • Don't create maintenance burdens with your comments. The above example has the actual filename in the code, so you'd have to change both the code and the comment if the filename changed.

References

In IT@JCU, we follow the official standards and conventions. Here are some useful references for Python style conventions, as expected in our subjects (PEP = Python Enhancement Proposal).

Where we refer to other style guides or conventions, these are for useful reference and comparison. Anything prescribed by other standards are not necessarily applicable to us.

Google Python Style Guide

Google's Python Style Guide is great and has some useful explanations. While we do not follow everything it prescribes, here are some highlights to help understand "why".

  • "Optimise for readability, not conciseness." - Comprehensions

  • "Use default iterators and operators for types that support them, like lists, dictionaries, and files." E.g., for line in file Iterators

  • "Conditional expressions (sometimes called a 'ternary operator') are okay to use for simple cases. Each portion must fit on one line: true-expression, if-expression, else-expression. Use a complete if statement when things get more complicated." - Conditional expressions

  • "Use the 'implicit' false if at all possible, e.g., if values instead of if values != [] - True/False evaluations

  • "The final place to have comments is in tricky parts of the code. If you’re going to have to explain it at the next code review, you should comment it now. Complicated operations get a few lines of comments before the operations commence. Non-obvious ones get comments at the end of the line. Comments should be as readable as narrative text, with proper capitalisation and punctuation. In many cases, complete sentences are more readable than sentence fragments." - Comments

  • "Function names, variable names, and filenames should be descriptive; avoid abbreviation. In particular, do not use abbreviations that are ambiguous or unfamiliar to readers outside your project, and do not abbreviate by deleting letters within a word." - Naming