Main Menu

CS 210 Style Guide for Python Programs

Most software development organizations have coding guidelines that constitute a house style. These may be based on some more widely used standard, but often they will also incorporate norms and preferences developed by a particular organization.

Guidelines for CS 211 projects are based on PEP 8, with some customizations.

Some parts of this document refer to classes and methods, which we will cover in CS 211. You may skip over parts of the style guide that are not applicable to CS 210 projects.

Living Document

This document represents my best effort at defining style guidelines for CIS 210, but it will almost certainly require revision. We may find that we need some additional rules, or that some existing rules are annoyingly strict, or that something is not as clear as it should be. Students are encouraged to suggest revisions. Meanwhile, the standard for any particular project is the version of this document as of the project due date. If the guidelines change between distribution of a project and its due date, I will use Piazza to announce whether the change applies to the current project, and to the extent possible I will avoid making you revise projects that have already been started.

Basics: When in doubt, follow PEP 8

PEP 8 is a widely used standard for Python code. It provides guidelines for all variety of matters from indentation to spacing in expressions.

PEP 8 refers to PEP 257 for docstring conventions. We will also follow PEP 257 conventions. We will not specify function and method type signatures in docstrings (see below for the alternative), but when appropriate we may use docstring comments to say more about arguments and return values.

Function headers

We will use Python type annotations (also known as type hints) to specify the signature (argument and return types) of functions and methods. Type annotations are a relatively new feature of Python, but they are supported well by PyCharm.

Type annotations make it possible to specify the signature of a function very compactly, compared to describing argument types in a docstring. Instead of this:


def frobnaz(n, s):
    """frob the naz.
    :n:  integer, frob it this many times
    :s:  string from which we extract the naz
    :returns: string in which the naz has been frobbed
    """
    

we will write


def frobnaz(n: int, s: str) -> str:
    """returns copy s with its frob nazzed n times"""
    

An absent return type is equivalent to specifying that the function or method returns None.

Method headers (CS 211)

Method headers follow the same rules as function signatures with the following exceptions:

  • The self argument of a regular method should not have a type hint.
  • Similarly, the cls argument of a class method should not have a type hint.

Mutation

A function or method that returns a result should not modify its input arguments. For example, if I write this function header:


def jazziest(musicians: List[Musician]) -> Musician:
    

it is implicit that the input list of musicians is not modified.

Occasionally returning a result and modifying an argument in the same function is helpful for writing concise and efficient code. The pop method for lists is an example of this. This is permissible but must be clearly and explicitly documented in the header function, and to the extent possible the function name should suggest that it modifies its input. For example, if the above function returned the jazziest musician an also removed it from the list, the function header and docstring might look like this:


def pull_jazziest(musicians: List[Musician]) -> Musician:
   """Remove and return jazziest musician from musicians"""
    

Classes: Public and quasi-private attributes (CS 211)

In accordance with PEP 8, object fields and methods that are meant to be public (that is, accessed from code outside the class or its subclasses) should begin with a lowercase letter. Names of fields and methods that are not meant to be accessed directly by code outside a class should begin with a single underscore. (These fields and methods might be declared private in a language with encapsulation, such as Java or C++, but Python does not provide encapsulation.)

In addition to the access categories described in PEP 8, we add two that can only be specified in docstrings:

  • A docstring may describe a class as immutable, in which case its public fields may be accessed but not modified by code outside the class.
  • In a class that is not immutable, individual fields may be described as read-only, meaning that outside code may access them but must not directly change them.

Tests

We will use doctests in CS 210. (We will switch from doctests to a unit test framework in CS 211.) This means that unit tests for functions will be included in function docstrings, like this:


def counts(column: list[str]) -> dict[str, int]:
    """Returns a dict with counts of elements in column.

    >>> counts(["dog", "cat", "cat", "rabbit", "dog"])
    {'dog': 2, 'cat': 2, 'rabbit': 1}
    """
    

In many cases I will provide a starter test suite with a project. The provided test suite is never exhaustive, and passing the provided test cases is not a guarantee that a project is correct. Students are strongly encouraged to add their own test cases, and may sometimes be required to do so (that is, designing and implementing good test cases may be part of the project requirements).