Python Coding Style Guide - WheatonCS/Lexos GitHub Wiki
This guide gives Lexos coders the correct coding conventions to use when adding to and editing the Python code for Lexos.
- PEP8
- Variable Naming
- Documentation
- Keep Your Functions Small
- Use Keyword Parameters
- Make Sure Your Editor is Happy with Your Code
- Avoid Mutating Your Variable
This standard needs to be satisfied in order for your code to be merged into the master
branch. (This standard is checked by flake8
with the pep8-naming
plugin.)
PEP8 needs to be strictly followed, since it is the most comprehensive and widely accepted style for the Python community.
- The original document of PEP8: PEP8 on Python.org.
- Another Style Guide for PEP8: PEP8.org
Here are some of the basic coding conventions from PEP8:
-
Naming:
-
Function (and method) names should be lowercase, with words separated by an underscore:
def function_name()
-
Local variable names should be lowercase, with words separated by an underscore:
local_variable = 1
-
Constants should be written in all capital letters using underscores to seperate words:
EXAMPLE_CONSTANTS = "constant string"
-
Class names should normally follow the CapWords convention:
class ExampleClass
-
-
Comments:
-
Each line of a block comment starts with a
#
and a single space. (Do not use"""
or'''
for block comments, they are used for docstrings):# hello world # here is the correct way to write a block comment # still in my block comment # this is the last line of my block comment
-
Inline comments should be separated by at least two spaces from the statement. They should start with a
#
and a single space:x = x + 1 # this is the correct way to write and inline comment
-
-
Individual Lines:
- The max line length should be 79 characters.
- Want to see how to wrap your long lines? Click here!
- Your editor's will guide you to indenting correctly when you insert a line break in the middle of a line
This standard needs to be satisfied in order for your code to be merged into the master
branch
YES 👍:
for index, element in enumerate(my_list):
pass
NO 👎:
for i, e in enumerate(l):
pass
YES 👍:
def example(input_list):
length = len(input_list)
NO 👎:
def example(list): # list is a type name
len = len(list) # len is a function name
YES 👍:
def sort(unsorted_list):
pass
def enable_files(file_ids):
pass
NO 👎:
def sort(l):
pass
def enable_files(ids):
pass
- The
id
attribute should use lowercase words with an underscore. - The
class
attribute should use lowercase words with a hyphen.
YES 👍:
<p id="example_id" class="example-class"/>
NO 👎:
<p id="exampleid" class="exampleClass"/>
Follow the standard for PyDocStyle
This standard needs to be satisfied in order for your code to be merged into the master
branch.
(This standard is checked by flake8
with the flake8_docstrings
plugin.)
- Code in the comments is confusing for the reader, because they cannot distinguish comments from unused code
- If we need old code, we have version control (
git
) so we can always find the old code. Leaving old code the in comments is redundant - For experimental code, you can open an issue. If we decide your idea is really good, but want to save it to write later, we can always open a new page in Wiki
Comment syntax follows the Sphinx-style docstrings (inherited from PyCharm as the standard development editor for Lexos). Follow the link and scroll to the end for instructions for implementing Sphinx-style docstrings in VS Code.
YES 👍:
def __init__(self, word_list, file_name):
"""Calculate all the information of the given file.
:param word_list: a dictionary map word to word count representing the
word count of particular file
:param file_name: the file name of that file
"""
NO 👎:
def __init__(self, word_list, file_name):
"""
Calculate all the information of the given file.
Args:
word_list: a dictionary map word to word count representing the
word count of particular file
file_name: the file name of that file
Returns:
None
"""
The formal syntax of the docstring should be as follows:
"""One line summary: starts with capital letter, ends with period; the next line needs to be empty.
Multiline description (optional).
:para para_name1: para description 1 (optional).
:para para_name2: para description 2 (optional).
:return: description of the variable returned (optional).
"""
or
"""One line doc string: starts with capital letter, ends with period."""
Typically dividing your function into smaller functions will help you to modulize your application.
A good rule of thumb is to make sure your function is no longer than 30 lines. This will make keeping track of what you are doing much easier.
A good rule of thumb is that you cannot have more than 12 spaces indenting a line (no more than a 3 indenting structures).
The more indentations your function has the less readable Python becomes. It will also be more difficult to keep your line under 79 characters.
YES 👍:
class ExampleClass:
# create private helper function when your logic is too complicated
function __do_something_for_elem__(element): # first indenting structure
if element.property is True: # second indenting structure
do_lots_of_things() # third indenting structure
function do_something_for_list(input_list): # first indenting structure
for element in input_list: # second indenting structure
__do_something_for_elem__(element) # third indenting structure
NO 👎:
class ExampleClass:
function do_something_for_list(input_list): # first indenting structure
for element in input_list: # second indenting structure
if element.property is True: # third indenting structure
do_lots_of_things() # forth indenting structure, NOOOOOO!
Typically using keyword parameters will make your code easier to understand:
YES 👍:
generate_matrix(use_freq=True,
token_type="by_gram",
token_size=2)
NO 👎:
generate_matrix(True, "by_gram", 2)
There are some exceptions:
YES 👍:
sin_of_x = sin(x)
NO 👎:
sin_of_x = sin(value=x)
In other words, make sure to follow basic Python coding conventions. When you are done with your code, your editor's linter should not give you any errors or warnings. (No squiqqly lines will appear in your code.)
YES 👍:
if some_variable is True:
other_variable = False
else:
other_variable = True
print(other_variable) # The linter is happy with your cod
NO 👎:
if some_variable is True:
other_variable = False
print(other_variable) # The linter will give you an warning here
As hard as it is to believe, in most cases in our code you really do not need to mutate your variable.
YES 👍:
def zip_and_shift_to_positive(x_coords, y_coords):
"""
Current function zips two lists (Xs and Ys) to xy_coord_list
and then shifts the whole graph to make all the coordinates positive
"""
# zips list
xy_coord_list = zip(x_coords, y_coords)
# shifts the list
x_shift = min(x_coords)
y_shift = min(y_coords)
positive_coord_list = [[x - x_shift, y - y_shift] for x, y in coord_list]
return positive_coord_list
NO 👎:
def zip_and_shift_to_positive(x_coords, y_coords):
x_shift = min(x_coords)
y_shift = min(y_coords)
# should use list comprehension
positive_coord_list = []
for index in range(len(x_coords)):
# very dangerous, since list is pass by refrence,
# editing lists in functions can easily render unwanted result
x_coords[i] -= x_shift
y_coords[i] -= y_shift
positive_coord_list.append([x_coords[i], y_coords[i]]) # append is much slower than list comprehension
return positive_coord_list
YES 👍:
my_sorted_list = sorted(my_list)
NO 👎:
my_list = sorted(my_list)
When you give your variable a new name, you are giving the variable a more accurate description, which therefore makes the code more clear.
Python gives you the freedom to mutate the type of a variable (since Python is dynamically typed), but a majority of programmers still think static typing provides better security to the program.
If you mutate the type of the variable, it is not the same thing as before. Therefore, you should give the variable a new name.
YES 👍:
json_dict = {'hello': 'lala', 'tata': 'haha'}
json_str = json.dump(json_dict)
NO 👎:
json_dict = {'hello': 'lala', 'tata': 'haha'}
json_dict = json.dump(json_dict)
As you can see, when you do a json.dump
on the dict, it becomes a string.
Therefore a more accurate description of the variable is not json_dict
, but json_str