In 61A, each project has a composition score, worth 3 points, that is graded on the style of your code. This document provides some guidelines.

After your code works, you should strive to do two things:

  1. Make your code easier to read
  2. Make your code concise
  3. Make your code efficient (optional for this class)

Sometimes these goals conflict with each other, and sometimes there are exceptions to the rules. Whatever you do, you should always try to make your code easy to read -- use your judgement.

Some of these guidelines will have one or more of the following marks:

Finally, here is a link to to PEP-8, the official Python style guide.

Names and variables

  1. Meaningful names: variable and function names should be self-descriptive:

    # bad -- don't do this!
    a, b, m = 100, 0, 0
    thing = 'hello world'
    stuff = lambda x: x % 2
    
    # good
    goal, score, opp_score = 100, 0, 0
    greeting = 'hello world'
    is_even = lambda x: x % 2
    
  2. Indices and mathematical symbols: using one-letter names and abbreviations is okay for indices, mathematical symbols, or if it is obvious what the variables are referring to.

    i = 0         # a counter for a loop
    x, y = 0, 0   # x and y coordinates
    p, q = 5, 17  # mathematical names in the context of the question
    

    In general, i, j, and k are the most common indices used.

  3. Avoid letters 'o' and 'l': do not use them by themselves as names:

    # bad!
    o = O + 4     # letter 'O' or number 0?
    l = l + 5     # letter 'l' or number 1?
    
  4. Don't create unnecessary variables: for example,

    # bad!
    result = answer(argument)
    return result
    
    # good!
    return answer(argument)
    

    However, if it is unclear what your code is referring to, or if the expression is too long, you should create a variable:

    # bad!
    do_something(lambda x: x % 49 == 0, (total + 1) // 7)
    
    # good!
    divisible_49 = lambda x: x % 49 == 0
    score = (total + 1) // 7
    do_somethig(divisible_49, result)
    
  5. Avoid profanity: don't leave it in your code. Even if you're really frustrated.

    # bad!
    eff_this_class = 666
    
  6. NP: Use lower_case_and_underscores for variables and functions:

    # bad!
    TotalScore = 0
    finalScore = 1
    
    def Mean_Strategy(score, opp):
        ...
    
    # good!
    total_score = 0
    final_score = 1
    
    def mean_strategy(score, opp):
        ...
    
  7. NP: Use CamelCase for classes:

    # bad!
    class example_class:
        ...
    
    # good!
    class ExampleClass:
        ...
    

Spacing and Indentation

Whitespace style might seem superfluous, but using whitespace in certain places (and omitting it in others) will often make it easier to read code. In addition, since Python code depends on whitespace (e.g. indentation), it requires some extra attention. For that reason, this section has quite a few guidelines that you should consider.

  1. Use spaces, not tabs for indentation: our starter code always uses 4 spaces instead of tabs. If you use both spaces and tabs, Python will raise an IndentationError.
  2. P: Use 4 spaces per indentation level: technically, Python allows you to use any number of spaces as long as you are consistent across an indentation level. The conventional style is to use 4 spaces.
  3. Keep lines under 80 characters long: other conventions use 70 or 72 characters, but 80 is usually the upper limit.
  4. Don't double-space code: that is, do not insert a blank line in between lines of code. Personally, I find that harder to read.
  5. N: Use spaces between primitive operators: always use spaces between + and -. Depending on how illegible expressions get, you can use your own judgement for *, /, and ** (as long as it's easy to read at a glance, it's fine).

    # bad!
    x=a+b*c*(a**2)/c-4
    
    # good!
    x = a + b*c*(a**2) / c - 4
    
  6. N: Spacing lists: When using tuples, lists, or function operands, leave one space after each comma ,:

    # bad!
    tup = (x,x/2,x/3,x/4)
    
    # good!
    tup = (x, x/2, x/3, x/4)
    
  7. NP: If a line gets two long, you have two options. If you are using parentheses or braces with multiple elements, you can continue them onto the next line:

    def func(a, b, c, d, e, f,
             g, h, i):
        # body
    
    tup = (1, 2, 3, 4, 5,
           6, 7, 8)
    names = ('alice',
             'bob',
             'eve')
    

    Notice that the subsequent lines line up with the start of the sequence. If the above rule does not apply, you can use Python's \ operator:

    total = this_is(a, very, lengthy) + line + of_code \
                + so_it - should(be, separated) \
                + onto(multiple, lines)
    

    Where you put the \ in relation to binary operators (e.g. hi \ + bye versus hi + \ bye) will vary from person to person -- for our class, it doesn't matter.

  8. NP: Leave a blank line between the end of a function or class and the next line:

    def example():
        return 'stuff'
    
    x = example() # notice the space above
    
  9. N: Don't leave whitespace at the end of a line.

Repetition

In general, don't repeat yourself (DRY). It wastes space and can be computationally inefficient.

  1. DON'T repeat complex expressions:

    if a + b - 3 * h / 2 % 47 == 4:
        total += a + b - 3 * h / 2 % 47
        return total
    

    Instead, store the expression in a variable:

    turn_score = a + b - 3 * h / 2 % 47
    if turn_score == 4:
        total += turn_score
        return total
    

    This will also make your code more readable.

  2. Don't repeat computationally-heavy function calls:

    if takes_one_minute_to_run(x) != ():
        first = takes_one_minute_to_run(x)[0]
        second = takes_one_minute_to_run(x)[1]
        third = takes_one_minute_to_run(x)[2]
    

    Instead, store the expression in a variable:

    result = takes_one_minute_to_run(x)
    if result != ():
        first = result[0]
        second = result[0]
        third = result[0]
    
  3. DON'T have the same code in both the if and the else clause of a conditional:

    if pred:            # bad!
        print('stuff')
        x += 1
        return x
    else:
        x += 1
        return x
    

    Instead, pull the line(s) out of the conditional:

    if pred:            # good!
        print('stuff')
    x += 1
    return x
    

Comments

Recall that Python comments begin with the # sign. Keep in mind that the triple-quotes are technically strings, not comments. Comments can be helpful for explaining ambiguous code, but there are some guidelines for when to use them.

  1. P: Put docstrings only at the top of functions: docstrings are denoted by triple-quotes at the beginning of a function or class:

    def average(fn, samples):
        """Calls a 0-argument function SAMPLES times, and takes
        the average of the outcome."""
    

    You should not put docstrings in the middle of the function -- only put them at the beginning.

  2. Remove commented-out code from final version: you can comment lines out when you are debugging. When you are turning in your project, take all commented lines out (including TODOs) -- this makes it easier for readers to read your code.
  3. Don't write unnecessary comments: for example, the following is bad:

    def example(y):
        x += 1            # increments x by 1
        return square(x)  # returns the square of x
    

    Your actual code should be self-documenting -- try to make it as obvious as possible what you are doing without resorting to comments. Only use comments if something is not obvious or needs to be explicitly emphasized

Control Structures

  1. DON'T compare a boolean variable to True or False:

    if pred == True:   # bad!
        ...
    if pred == False:  # bad!
        ...
    

    Instead, do this:

    if pred:           # good!
        ...
    if not pred:       # good!
        ...
    
  2. DON'T do this:

    if pred:            # bad!
        return True
    else:
        return False
    

    Instead, do this:

    return pred         # good!
    
  3. (related to the previous:) DON'T do this:

    if num != 49:
        total += example(4, 5, True)
    else:
        total += example(4, 5, False)
    

    In the example above, the only thing that changes between the conditionals is the boolean at the end. Instead, do this:

    total += example(4, 5, num != 49)
    
  4. DON'T use a while loop when you should use an if:

    while pred:
        x += 1
        return x
    

    Instead, use an if

    if pred:
        x += 1
        return x
    
  5. P: DON'T use parentheses with conditional statements:

    if (x == 4):
        ...
    elif (x == 5):
        ...
    while (x < 10):
        ...
    

    Parentheses are not necessary in Python conditionals (they are in other languages though).

Miscellaneous

  1. P: Do not use semicolons. This is not C/C++/Java/etc.
  2. P: Use is and is not for None, not == and !=.
  3. P: Use the "implicit" False value when possible. Examples include empty containers like [], (), {}, set().

    if lst:       # if lst is not empty
        ...
    if not tup:   # if tup is empty
        ...
    
  4. P: Generator expressions are okay for simple expressions: this includes list comprehensions, dictionary comprehensions, set comprehensions, etc. Generator expressions are neat ways to concisely create lists. Simple ones are fine and even encouraged:

    ex = [x*x for x in range(10)]
    L = [pair[0] + pair[1] for pair in pairs if len(pair) == 2]
    

    However, complex generator expressions are very hard to read, even illegible. As such, do not use generator expressions for complex expressions.

    L = [x + y + z for x in nums if x > 10 for y in nums2 for z in nums3 if y > z]
    

    Use your best judgement.