Python Code Quality: Style Conventions and Linting Tools

Leapcell: The Best of Serverless Web Hosting Guide to Python Code Style and Checking Tools The Python code style guide is not set in stone. It evolves continuously with the development of the language. Some old conventions are gradually phased out, and new ones keep emerging. At the same time, many projects have their own coding style guides. When there are conflicts, the project-specific guides should be followed first. However, there is an important principle to keep in mind: "The stupid persistence in consistency is the monster of ignorance", which is a profound insight from Guido. Since code is often read more frequently than it is written, the core goal of the style guide is to improve code readability and keep all kinds of Python code consistent. As PEP20 says, "Readability counts". The style guide emphasizes consistency. While it is important to be consistent with the guide, consistency within a project is even more crucial, and consistency within a module or function is of utmost importance. Of course, there are also reasonable situations where some parts of the style guide can be ignored. For example: applying the guide will reduce code readability; being consistent with the surrounding historical code will damage the overall structure; the code appeared before the guide and there is no need to modify it; the code needs to be compatible with the features of older versions of Python, etc. It should be noted especially that don't break backward compatibility just to follow PEP! Code Layout Indentation Use 4 spaces uniformly for each level of indentation. Vertical implicit indentation or hanging indentation can be used within parentheses. When using hanging indentation, the first line should not contain parameters, and subsequent lines need to be indented. For example: # Align with the starting delimiter leapcell_foo = long_function_name(var_one, var_two, var_three, var_four) # Use more indentation to distinguish it from other code def long_function_name( var_one, var_two, var_three, var_four): print(var_one) # Hanging indentation, an additional level of indentation must be added leapcell_foo = long_function_name( var_one, var_two, var_three, var_four) It should be noted that for consecutive lines, the indentation of four spaces is not mandatory. When an if statement spans multiple lines, the if keyword followed by a space and then the left parenthesis form the indentation. There are multiple formats for subsequent lines, such as no additional indentation, adding comments, or adding additional indentation (recommended): # Without additional indentation if (this_is_one_thing and that_is_another_thing): do_something() # Add comments # supporting syntax highlighting. if (this_is_one_thing and that_is_another_thing): # Since both conditions are true, we can frobnicate. do_something() # Add additional indentation, recommended if (this_is_one_thing and that_is_another_thing): do_something() When the closing parenthesis is on a new line, the format of backing up the closing parenthesis is recommended: # Back up the closing parenthesis leapcell_my_list = [ 1, 2, 3, 4, 5, 6, ] leapcell_result = some_function_that_takes_arguments( 'a', 'b', 'c', 'd', 'e', 'f', ) Spaces or Tabs Spaces are the preferred way of indentation. Only use tabs when it is necessary to be consistent with existing code indented with tabs. Mixing tabs and spaces for indentation is strictly prohibited in Python 3; code with mixed indentation in Python 2 should all be converted to space indentation. The Python 2 command-line interpreter can issue warnings about illegal mixed indentation through the -t option, and the -tt option upgrades the warning to an error. It is recommended to use these two options. At the same time, the pep8 and autopep8 modules are recommended. ### Maximum Line Width The maximum width of all code lines is limited to 79 characters, and the line length of docstrings or comments is limited to 72 characters. If the team reaches an agreement, the line width can be increased from 80 to 100 characters (at most 99 characters), but docstrings and comments are still recommended to maintain a length of 72 characters. The Python standard library adopts a more conservative line width limit of 79 characters (72 characters for docstrings/comments). The preferred way to continue lines is to use parentheses, square brackets, and curly braces. The backslash can be used in appropriate scenarios, such as in the with statement: with open('/path/to/some/file/you/want/to/read') as leapcell_file_1, \ open('/path/to/some/file/being/written', 'w') as leapcell_file_2: leapcell_file_2.write(leapcell_file_1.read()) Line Continuation of Binary Operators When continuing a line at a binary operator, the traditional recommendati

May 7, 2025 - 05:30
 0
Python Code Quality: Style Conventions and Linting Tools

Image description

Leapcell: The Best of Serverless Web Hosting

Guide to Python Code Style and Checking Tools

The Python code style guide is not set in stone. It evolves continuously with the development of the language. Some old conventions are gradually phased out, and new ones keep emerging. At the same time, many projects have their own coding style guides. When there are conflicts, the project-specific guides should be followed first. However, there is an important principle to keep in mind: "The stupid persistence in consistency is the monster of ignorance", which is a profound insight from Guido. Since code is often read more frequently than it is written, the core goal of the style guide is to improve code readability and keep all kinds of Python code consistent. As PEP20 says, "Readability counts".

The style guide emphasizes consistency. While it is important to be consistent with the guide, consistency within a project is even more crucial, and consistency within a module or function is of utmost importance. Of course, there are also reasonable situations where some parts of the style guide can be ignored. For example: applying the guide will reduce code readability; being consistent with the surrounding historical code will damage the overall structure; the code appeared before the guide and there is no need to modify it; the code needs to be compatible with the features of older versions of Python, etc. It should be noted especially that don't break backward compatibility just to follow PEP!

Code Layout

Indentation

  • Use 4 spaces uniformly for each level of indentation.
  • Vertical implicit indentation or hanging indentation can be used within parentheses. When using hanging indentation, the first line should not contain parameters, and subsequent lines need to be indented. For example:
# Align with the starting delimiter
leapcell_foo = long_function_name(var_one, var_two,
                                 var_three, var_four)

# Use more indentation to distinguish it from other code
def long_function_name(
        var_one, var_two, var_three,
        var_four):
    print(var_one)

# Hanging indentation, an additional level of indentation must be added
leapcell_foo = long_function_name(
    var_one, var_two,
    var_three, var_four)

It should be noted that for consecutive lines, the indentation of four spaces is not mandatory.

  • When an if statement spans multiple lines, the if keyword followed by a space and then the left parenthesis form the indentation. There are multiple formats for subsequent lines, such as no additional indentation, adding comments, or adding additional indentation (recommended):
# Without additional indentation
if (this_is_one_thing and
    that_is_another_thing):
    do_something()

# Add comments
# supporting syntax highlighting.
if (this_is_one_thing and
    that_is_another_thing):
    # Since both conditions are true, we can frobnicate.
    do_something()

# Add additional indentation, recommended
if (this_is_one_thing
        and that_is_another_thing):
    do_something()
  • When the closing parenthesis is on a new line, the format of backing up the closing parenthesis is recommended:
# Back up the closing parenthesis
leapcell_my_list = [
    1, 2, 3,
    4, 5, 6,
]
leapcell_result = some_function_that_takes_arguments(
    'a', 'b', 'c',
    'd', 'e', 'f',
)

Spaces or Tabs

  • Spaces are the preferred way of indentation.
  • Only use tabs when it is necessary to be consistent with existing code indented with tabs.
  • Mixing tabs and spaces for indentation is strictly prohibited in Python 3; code with mixed indentation in Python 2 should all be converted to space indentation. The Python 2 command-line interpreter can issue warnings about illegal mixed indentation through the -t option, and the -tt option upgrades the warning to an error. It is recommended to use these two options. At the same time, the pep8 and autopep8 modules are recommended. ### Maximum Line Width
  • The maximum width of all code lines is limited to 79 characters, and the line length of docstrings or comments is limited to 72 characters.
  • If the team reaches an agreement, the line width can be increased from 80 to 100 characters (at most 99 characters), but docstrings and comments are still recommended to maintain a length of 72 characters. The Python standard library adopts a more conservative line width limit of 79 characters (72 characters for docstrings/comments).
  • The preferred way to continue lines is to use parentheses, square brackets, and curly braces. The backslash can be used in appropriate scenarios, such as in the with statement:
with open('/path/to/some/file/you/want/to/read') as leapcell_file_1, \
     open('/path/to/some/file/being/written', 'w') as leapcell_file_2:
    leapcell_file_2.write(leapcell_file_1.read())

Line Continuation of Binary Operators

When continuing a line at a binary operator, the traditional recommendation is to break after the operator, but this may affect readability. Following the mathematical tradition, breaking before the binary operator usually makes the code more readable. In Python code, as long as it is consistent locally, it is allowed to break before or after the operator. For new code, it is recommended to adopt the Knuth style of breaking before the operator:

# The operator is easy to match with the operands
leapcell_income = (gross_wages
                  + taxable_interest
                  + (dividends - qualified_dividends)
                  - ira_deduction
                  - student_loan_interest)

Blank Lines

  • The definitions of top-level functions and classes are separated by two blank lines.
  • The method definitions in a class are separated by a single blank line.
  • Additional blank lines can be used to separate different function groups or logical blocks within a function as needed, but excessive use should be avoided. ### Source File Encoding
  • The code in the core Python release should always use UTF-8 (ASCII in Python 2).
  • No encoding declaration is required for ASCII files (Python 2) or UTF-8 (Python 3). Non-default encodings in the standard library are only used for testing or for comments and docstrings containing non-ASCII characters (such as author names). In this case, try to use \x, \u, \U or \N.
  • For Python 3.0 and later versions, referring to PEP 3131, ASCII identifiers should be used in the standard library, and only English letters should be used as much as possible. Strings and comments must also be ASCII, except for testing non-ASCII functions and author names in non-Latin alphabets. ### Imports
  • Import statements should occupy a single line:
# Correct example
import os
import sys

# Incorrect example
import sys, os

The following form can also be used:

from subprocess import Popen, PIPE
  • Import statements should be at the top of the file, after module comments and docstrings, and before module global variables and constants. The import order is: standard library imports, related third-party library imports, local library imports, and blank lines are used to separate each group of imports.
  • all-related imports should be placed after other imports.
  • It is recommended to use absolute path imports because they are more readable and perform better (or at least can provide better error messages):
import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example

When the absolute path is too long, relative path imports can be used:

from . import sibling
from .sibling import example

The code in the standard library should avoid complex package layouts and always use absolute imports. Implicit relative imports should be avoided and removed in Python 3.

  • When importing classes, the following methods can be used:
from myclass import MyClass
from foo.bar.yourclass import YourClass

If there are local naming conflicts, you can use:

import myclass
import foo.bar.yourclass
  • Wildcard imports (from import *) are prohibited because they will make the namespace unclear. They can only be considered when republishing the external API. ### String Quotations
  • In Python, single-quoted strings and double-quoted strings have the same function. Try to avoid backslashes in strings to improve readability.
  • According to PEP 257, triple-quoted strings should use double quotes. ### Spaces in Expressions and Statements
  • Avoid adding spaces inside parentheses:
# Correct example
spam(ham[1], {eggs: 2})
# Incorrect example
spam( ham[ 1 ], { eggs: 2 } )
  • There should be no space between the trailing comma and the closing parenthesis:
# Correct example
leapcell_foo = (0,)
# Incorrect example
leapcell_bar = (0, )
  • Avoid spaces before commas, colons, and semicolons:
# Correct example
if x == 4: print x, y; x, y = y, x
# Incorrect example
if x == 4 : print x , y ; x , y = y , x
  • In indexing operations, the colon as an operator should have consistent spaces before and after (it is recommended not to use spaces):
# Correct example
ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:]
ham[lower:upper], ham[lower:upper:], ham[lower::step]
ham[lower+offset : upper+offset]
ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
ham[lower + offset : upper + offset]

# Incorrect example
ham[lower + offset:upper + offset]
ham[1: 9], ham[1 :9], ham[1:9 :3]
ham[lower : : upper]
ham[ : upper]
  • There should be no space before the left parenthesis of a function call:
# Correct example
spam(1)
dct['key'] = lst[index]

# Incorrect example
spam (1)
dct ['key'] = lst [index]
  • Multiple spaces should not be added before and after assignment operators and other operators for the purpose of alignment:
# Correct example
leapcell_x = 1
leapcell_y = 2
leapcell_long_variable = 3

# Incorrect example
leapcell_x             = 1
leapcell_y             = 2
leapcell_long_variable = 3

Code Checking Tools

In the process of software project maintenance, it is crucial to maintain a consistent code style and testing standards. It can reduce the maintenance pressure, help new developers quickly get familiar with the project, and ensure the quality of the application. Using external libraries to check the code quality is an effective means to maintain the maintainability of the project. The following are commonly used Python code style checking and formatting tools.

Code Style Checking Tools

  1. Pylint: A library used to check violations of the PEP 8 specification and common errors. It can be integrated into popular editors and IDEs, and also supports running from the command line. After installing it via pip install pylint, use pylint [options] path/to/dir or pylint [options] path/to/module.py for checking, and the results will be output to the console. You can also customize the checking rules through the pylintrc configuration file.
  2. Flake8: It integrates PEP 8, Pyflakes (similar to Pylint), McCabe (a code complexity checker), and third-party plugins, and is used to check the style and quality of Python code. The installation command is pip install flake8, and execute flake8 [options] path/to/dir or flake8 [options] path/to/module.py to view errors and warnings. It supports customizing the checking content through the configuration file. The documentation provides useful commit hooks, which can be integrated into the development workflow. It can also be integrated into editors or IDEs through plugins (such as the Flake8 plugin for Sublime Text).
  3. Isort: It can sort the imported libraries in the project alphabetically and correctly divide the parts such as the standard library, third-party libraries, and self-built libraries, improving code readability. Install it using pip install isort, and run isort path/to/module.py. You can also handle multi-line imports of libraries by configuring the .isort.cfg file. It also supports integration with popular editors and IDEs.

Code Formatting Tools

  1. Autopep8: It can automatically format the code of the specified module, including re-indenting lines, fixing indentation, removing redundant spaces, and refactoring common comparison errors (such as boolean values and None values). Install it via pip install --upgrade autopep8, and execute autopep8 --in-place --aggressive --aggressive to format the code. The number of aggressive options determines the degree of control over the code style.
  2. Yapf: It can not only point out places that violate the PEP 8 specification but also reformat the code that is inconsistent in style but does not violate the specification to enhance readability. The installation command is pip install yapf, and use yapf [options] path/to/dir or yapf [options] path/to/module.py to format the code. It can be configured through a complete list of customization options.
  3. Black: A relatively new code checking tool. Similar to Autopep8 and Yapf, but with fewer customization options, and there is no need to manually determine the code style. It depends on Python 3.6+ and can also format Python 2 code. Install it via pip install black, and execute black path/to/dir or black path/to/module.py to optimize the code. You can refer to the limited customization options and configuration methods.

Test Coverage Checking Tool

Coverage: It is used to calculate the test coverage and provides multiple display methods, such as outputting to the console or an HTML page, and marking the un-covered code lines. It supports customizing the checking content through the configuration file. The installation command is pip install coverage, and execute coverage [path/to/module.py] [args] to run the program and view the results. coverage report -m can be used to view the un-covered code lines.

Leapcell: The Best of Serverless Web Hosting

Finally, I would like to recommend a platform that is most suitable for deploying Python services: Leapcell

Image description