A style guide is a set of rules that defines how code should be written. Following a style guide can make code more consistent, easier to read, and easier to maintain.
In this blog post, we will discuss the Python style guide, linting, and its importance.
Python Style Guide is a set of guidelines for Python code formatting and organization aimed at improving the readability, maintainability, and consistency of Python code. Here we cover topics such as indentation, comments, naming conventions, function and class definitions, and more. The guide recommends the use of clear, concise, and expressive code that follows the PEP 8 style guide, and provides specific examples and explanations of common coding patterns and practices. Overall, the Python Style Guide serves as a valuable resource for Python developers looking to write high-quality code that is easy to understand and maintain.
Naming Convention
Here are some common conventions for in Python:
Guidelines derived from Guido’s Recommendations
Type | Public | Internal |
---|---|---|
Packages | lower_with_under |
|
Modules | lower_with_under |
_lower_with_under |
Classes | CapWords |
_CapWords |
Exceptions | CapWords |
|
Functions | lower_with_under() |
_lower_with_under() |
Global/Class Constants | CAPS_WITH_UNDER |
_CAPS_WITH_UNDER |
Global/Class Variables | lower_with_under |
_lower_with_under |
Instance Variables | lower_with_under |
_lower_with_under (protected) |
Method Names | lower_with_under() |
_lower_with_under() (protected) |
Function/Method Parameters | lower_with_under |
|
Local Variables | lower_with_under |
Docstring
Docstrings are used to document modules, classes, functions, and methods.
Modules docstrings should be enclosed in triple quotes ("""
) and placed immediately below the object they are documenting. The docstring should provide a clear and concise description of the object’s purpose and behavior.
For modules and packages, the docstring should describe the contents of the module or package and provide usage examples if appropriate. For classes, the docstring should describe the class’s behavior and list its public methods and attributes. For functions and methods, the docstring should describe the arguments, return values, and any exceptions that may be raised.
Module Docstring
"""This module provides utility functions for working with strings."""
def function1():
pass
def function2():
pass
Class Docstring
class MyClass:
"""This is a sample class that demonstrates docstring formatting.
This class provides a simple example of how to format docstrings for classes.
"""
def __init__(self):
pass
def public_method(self):
"""This is a public method of the class.
This method does something useful and returns a result.
"""
pass
Function Docstring
def my_function(arg1, arg2):
"""This function does something.
Args:
arg1: The first argument.
arg2: The second argument.
Returns:
The return value. It returns something.
"""
pass
Here’s an example of a function docstring with Args
, Returns
, and Raises
sections:
def divide_numbers(numerator: float, denominator: float) -> float:
"""Divides the numerator by the denominator.
Args:
numerator: The numerator.
denominator: The denominator.
Returns:
The result of the division.
Raises:
ValueError: If the denominator is zero.
"""
if denominator == 0:
raise ValueError("Denominator cannot be zero.")
return numerator / denominator
Static Typing
Use static typing with type annotations to make the code more readable and maintainable. Type annotations help clarify the expected types of arguments and return values, and can also help catch certain types of errors before runtime. Here’s an example of how to use static typing with type annotations:
def add_numbers(x: int, y: int) -> int:
"""Adds two integers together and returns the result."""
return x + y
In this example, the function add_numbers()
takes two arguments of type int
, and returns an int
. The type annotations are added after the argument and return value names, separated by a colon. Type annotations can be added to variables, function/methods arguments, and return values.
Sure, here are some more examples of how to use static typing with type annotations:
a: int = 42
b: str = "hello"
c: List[int] = [1, 2, 3]
In this example, we declare three variables with type annotations. a
is an int
, b
is a str
, and c
is a list of int
s.
Function Argument and Return Types
def calculate_average(numbers: List[float]) -> float:
"""Calculates the average of a list of numbers."""
return sum(numbers) / len(numbers)
In this example, the function calculate_average()
takes a single argument numbers
that is a list of float
s, and returns a float
representing the average of the list.
Class Attributes and Methods
class Rectangle:
# No return type required for __init__
def __init__(self, width: float, height: float):
self.width = width
self.height = height
def area(self) -> float:
return self.width * self.height
In this example, we define a Rectangle
class with an __init__
method that takes two arguments, width
and height
, both of type float
. The class also has an area()
method that returns the area of the rectangle as a float
.
Useful suggestions
- Limit all lines to a maximum of 101 characters.
- Avoid using single-letter variable names, except for iterators.
- Use spaces around operators and after commas, but not directly inside parentheses or brackets.
- Use inline comments sparingly and only for clarifying complex code.
- Use docstrings to document modules, classes, functions, and methods.
- Avoid using global variables whenever possible.
- Use
is
andis not
to compare withNone
, rather than==
or!=
. - Use list comprehensions and generator expressions instead of
filter()
andmap()
functions. - Use the
with
statement for file operations and other resource-intensive operations. - Use the
logging
module for logging instead ofprint()
. - Use
if/raise
instead ofassert
. Userassert
only in Tests. - Check for conditions that should never occur.
- Use
pathlib
instead ofos.path
for path manipulation. - Use
f-string
instead offormat
/%
. Avoid using%
for string formatting, since it’s an older syntax that can be less readable and error-prone.
Linting
Linting refers to the practice of running code analysis tools to flag programming errors, bugs, stylistic errors, and suspicious usage in Python code.
By promoting quality and consistency, linting leads to more robust, maintainable, and bug-free code. The small upfront investment in linting pays huge dividends in the long term in software quality and developer productivity.
Here are some key reasons why linting is an important part of the software development process:
- Catch bugs and errors early - Linting helps catch syntax errors, undefined variables, type inconsistencies, and other issues that can lead to bugs. Finding issues before code goes to production is hugely valuable.
- Enforce coding standards - Linters can check code against style guides like PEP8 and highlight deviations. This helps teams keep code consistent and readable.
- Reduce technical debt - Linting helps avoid accumulating cruft, duplicate code, and messy/outdated patterns that make code harder to maintain. It prevents technical debt.
- Improve readability - Well-linter code with proper formatting and organization is much easier for developers to parse and understand quickly. Readability matters.
- Refactoring assistance - Linters can point out complex code and long functions that need to be refactored and simplified. Streamlines maintenance.
- Analyze program flow - Linters can track variable usage, uncover unreachable code, and analyze control flow to point out logical issues.
- Integrate with IDEs and CI/CD - Linting can integrate with developer workflows through editor plugins and continuous integration. Automatic linting provides rapid feedback.
Tools for python code linting
- pylint - This is one of the most widely used linters for Python. It analyzes code for errors, tries to enforce a coding standard, and also looks for code smells. See Pylint docs
- flake8 - This linter combines the capabilities of PyFlakes (to check for errors), pycodestyle (for style guide enforcement), and Ned Batchelder’s McCabe script (for complexity checking). See flake8 docs
- pydocstyle - Focuses mainly on linting docstrings based on PEP 257. Helps ensure consistent documentation. See pydocstyle docs
- mypy - A static type checker that looks for type errors. Can catch many bugs as part of lining. See mypy docs
- pyreverse - Creates UML diagrams to analyze large codebases. Helps enforce programming principles. See pyreverse docs
- yapf - Yet Another Python Formatter. Helps enforce coding style and formatting conventions. See yapf docs
- black - An auto-formatter that can enforce coding style as part of lining. See black docs
- pyupgrade - Helps upgrade Python syntax to newer versions automatically. Catches outdated code patterns. See pyupgrade docs
- bandit - Focuses on security issues by scanning for common vulnerabilities and exposures in Python code. See bandit docs
- isort - Helps enforce import sorting to group imports alphabetically and separated by type. Improves readability. See isort docs