CS 240: Data Structures and Algorithms
James Madison University, Spring 2013

CS240 Python Coding Conventions

For this course we will primarily follow the PEP 8 Style Guide for Python Code. This is the style guide that describes the coding standards for code to be included in the Python standard library. I encourage you to read that entire document, but you are only responsible for the boxed (and edited) excerpts included on this page. I also recommend taking a look at Google Python Style Guide. Again, you are not responsible for that material, but it has some good advice for writing well organized Python code.

Code lay-out

Executable programs for this course should define a main function that is called from within a conditional block like the following:

if __name__ == "__main__":
    main()
Under no circumstances should your modules include function calls that occur unconditionally. Such calls interfere with the use of PyDoc, and make it difficult for me to use testing code that imports your module.

PEP 8:
  Indentation

    Use 4 spaces per indentation level.

  Tabs or Spaces?

    Never mix tabs and spaces.

    For new projects, spaces-only are strongly recommended over tabs.  Most
    editors have features that make this easy to do.

  Maximum Line Length

    Limit all lines to a maximum of 79 characters.

    The preferred way of wrapping long lines is by using Python's implied line
    continuation inside parentheses, brackets and braces.  Long lines can be
    broken over multiple lines by wrapping expressions in parentheses. These
    should be used in preference to using a backslash for line continuation.
    Make sure to indent the continued line appropriately.  The preferred place
    to break around a binary operator is *after* the operator, not before it.
    Some examples:

    class Rectangle(Blob):

        def __init__(self, width, height,
                     color='black', emphasis=None, highlight=0):
            if (width == 0 and height == 0 and
                color == 'red' and emphasis == 'strong' or
                highlight > 100):
                raise ValueError("sorry, you lose")
            if width == 0 and height == 0 and (color == 'red' or
                                               emphasis is None):
                raise ValueError("I don't think so -- values are %s, %s" %
                                 (width, height))
            Blob.__init__(self, width, height,
                          color, emphasis, highlight)


Imports

PEP 8:
    - Imports should usually be on separate lines, e.g.:

        Yes: import os
             import sys

        No:  import sys, os

      it's okay to say this though:

        from subprocess import Popen, PIPE

    - Imports are always put at the top of the file, just after any module
      comments and docstrings, and before module globals and constants.


Whitespace in Expressions and Statements

PEP 8:
    Avoid extraneous whitespace in the following situations:

    - Immediately inside parentheses, brackets or braces.

      Yes: spam(ham[1], {eggs: 2})
      No:  spam( ham[ 1 ], { eggs: 2 } )

    - Immediately before a comma, semicolon, or colon:

      Yes: if x == 4: print x, y; x, y = y, x
      No:  if x == 4 : print x , y ; x , y = y , x

    - Immediately before the open parenthesis that starts the argument
      list of a function call:

      Yes: spam(1)
      No:  spam (1)

    - Immediately before the open parenthesis that starts an indexing or
      slicing:

      Yes: dict['key'] = list[index]
      No:  dict ['key'] = list [index]

    - More than one space around an assignment (or other) operator to
      align it with another.

      Yes:

          x = 1
          y = 2
          long_variable = 3

      No:

          x             = 1
          y             = 2
          long_variable = 3


  Other Recommendations

    - Always surround these binary operators with a single space on
      either side: assignment (=), augmented assignment (+=, -= etc.),
      comparisons (==, <, >, !=, <>, <=, >=, in, not in, is, is not),
      Booleans (and, or, not).

    - Use spaces around arithmetic operators:

      Yes:

          i = i + 1
          submitted += 1
          x = x * 2 - 1
          hypot2 = x * x + y * y
          c = (a + b) * (a - b)

      No:

          i=i+1
          submitted +=1
          x = x*2 - 1
          hypot2 = x*x + y*y
          c = (a+b) * (a-b)

    - Don't use spaces around the '=' sign when used to indicate a
      keyword argument or a default parameter value.

      Yes:

          def complex(real, imag=0.0):
              return magic(r=real, i=imag)

      No:

          def complex(real, imag = 0.0):
              return magic(r = real, i = imag)

    - Compound statements (multiple statements on the same line) are
      generally discouraged.

      Yes:

          if foo == 'blah':
              do_blah_thing()
          do_one()
          do_two()
          do_three()

      Rather not:

          if foo == 'blah': do_blah_thing()
          do_one(); do_two(); do_three()

    - While sometimes it's okay to put an if/for/while with a small
      body on the same line, never do this for multi-clause
      statements.  Also avoid folding such long lines!

      Rather not:

          if foo == 'blah': do_blah_thing()
          for x in lst: total += x
          while t < 10: t = delay()

      Definitely not:

          if foo == 'blah': do_blah_thing()
          else: do_non_blah_thing()

          try: something()
          finally: cleanup()

          do_one(); do_two(); do_three(long, argument,
                                       list, like, this)

          if foo == 'blah': one(); two(); three()


Comments

The point of comments is to clarify code that might otherwise be difficult for the reader to follow. Well organized Python code with informative variable names should require very few comments inside the bodies of functions and methods.

PEP 8:
    Comments that contradict the code are worse than no comments.  Always make
    a priority of keeping the comments up-to-date when the code changes!

    Comments should be complete sentences.  If a comment is a phrase or
    sentence, its first word should be capitalized, unless it is an identifier
    that begins with a lower case letter (never alter the case of
    identifiers!).

    If a comment is short, the period at the end can be omitted.  Block
    comments generally consist of one or more paragraphs built out of complete
    sentences, and each sentence should end in a period.

    You should use two spaces after a sentence-ending period.

    When writing English, Strunk and White apply.


  Block Comments

    Block comments generally apply to some (or all) code that follows them,
    and are indented to the same level as that code.  Each line of a block
    comment starts with a # and a single space (unless it is indented text
    inside the comment).

    Paragraphs inside a block comment are separated by a line containing a
    single #.

  Inline Comments

    Use inline comments sparingly.

    An inline comment is a comment on the same line as a statement.  Inline
    comments should be separated by at least two spaces from the statement.
    They should start with a # and a single space.

    Inline comments are unnecessary and in fact distracting if they state
    the obvious.  Don't do this:

        x = x + 1                 # Increment x

    But sometimes, this is useful:

        x = x + 1                 # Compensate for border


Documentation Strings

PEP 8:
    Conventions for writing good documentation strings (a.k.a. "docstrings")
    are immortalized in PEP 257 [3].

    - Write docstrings for all public modules, functions, classes, and
      methods.  Docstrings are not necessary for non-public methods, but you
      should have a comment that describes what the method does.  This comment
      should appear after the "def" line.

    - PEP 257 describes good docstring conventions.  Note that most
      importantly, the """ that ends a multiline docstring should be on a line
      by itself, and preferably preceded by a blank line, e.g.:

      """Return a foobang

      Optional plotz says to frobnicate the bizbaz first.

      """

    - For one liner docstrings, it's okay to keep the closing """ on the same
      line.

Module headers should include a brief description of the module as well as author and version information. A separate block comment should include your honor code statement.

Naming Conventions

Selecting informative names is one of the most important steps in writing readable, maintainable, and correct code. Variable names should generally be nouns, and should clearly describe what the variable is intended to contain. Single letter variable are almost never acceptable, with exception of loop index variables. It is usually a good idea to use plural nouns for variables that will contain collections, and singular nouns for variables that will contain individual objects.

Variable names should balance clarity with brevity. The name person is better than currentPersonObject. However, per is worse than either (percentage? person?, permitted?).

Function and method names should typically be verbs and should describe what the function or method is intended to accomplish.

Avoid selecting place-holder names with the intention of replacing them later. If you aren't sure what the variable or function should be named, that's often an indication that you don't have a clear idea of what the variable will hold or what the function should accomplish. Taking the time to figure out an appropriate name will clarify your thinking.

PEP 8 (CS240 changes in red):

  Naming Conventions

    Names to Avoid

      Never use the characters `l' (lowercase letter el), `O' (uppercase
      letter oh), or `I' (uppercase letter eye) as single character variable
      names.

      In some fonts, these characters are indistinguishable from the numerals
      one and zero.  When tempted to use `l', use `L' instead.

    Package and Module Names

      Modules should have short, all-lowercase names.  Underscores can be used
      in the module name if it improves readability.  Python packages should
      also have short, all-lowercase names, although the use of underscores is
      discouraged.

    Class Names

      Almost without exception, class names use the CapWords convention.
      Classes for internal use have a leading underscore in addition.

    Exception Names

      Because exceptions should be classes, the class naming convention
      applies here.  However, you should use the suffix "Error" on your
      exception names (if the exception actually is an error).

    Function Names
  
      Function names should be mixedCase.

    Method Names and Instance Variables

      Use the function naming rules. 

      Use one leading underscore only for non-public methods and instance
      variables.

    Constants

       Constants are usually defined on a module level and written in all
       capital letters with underscores separating words.  Examples include
       MAX_OVERFLOW and TOTAL.

    Designing for inheritance

      Always decide whether a class's methods and instance variables
      (collectively: "attributes") should be public or non-public.  If in
      doubt, choose non-public; it's easier to make it public later than to
      make a public attribute non-public.

      Public attributes are those that you expect unrelated clients of your
      class to use, with your commitment to avoid backward incompatible
      changes.  Non-public attributes are those that are not intended to be
      used by third parties; you make no guarantees that non-public attributes
      won't change or even be removed.

      With this in mind, here are the Pythonic guidelines:

      - Public attributes should have no leading underscores.

      - For simple public data attributes, it is best to expose just the
        attribute name, without complicated accessor/mutator methods.  Keep in
        mind that Python provides an easy path to future enhancement, should
        you find that a simple data attribute needs to grow functional
        behavior.  In that case, use properties to hide functional
        implementation behind simple data attribute access syntax.

        Note 1: Properties only work on new-style classes.
   

Programming Recommendations

PEP 8:
  
    - Don't compare boolean values to True or False using ==

        Yes:   if greeting:

        No:    if greeting == True:

        Worse: if greeting is True: