Pylint is a Python programming language quality checker that follows the PEP 8 style guide. With the primary objective of enhancing the readability and uniformity of the code, this article offers recommendations for writing understandable Python code. It is essential to follow standards that will make it easier for ourselves and others to comprehend accessible code since code is read far more frequently than it is created. This Blog explains 10 PEP 8 guidelines that can help make your Python code simpler to understand for others and how to use Pylint to verify them. Let's get going!
Installation
Make sure Python is set up on your computer before you install Pylint. The following command should be entered into your computer's command prompt (Windows) or terminal (Linux).
pip install pylint
Enter the following command to check the pylint installation.
Use Pylint
Once Pylint is installed, we can quickly use it by entering the following command along with the file name:
Pylint file_name.py
Coding conventions (PEP 8)
PEP 8 is a style manual that outlines the proper formatting for Python code to improve readability. We will only touch on a few major issues in this Blog, so I recommend checking out the guide if you want to learn more about the subject in depth.
The link is here: https://peps.python.org/pep-0008/
1. Multi-line statement
Python uses parenthesis (()), brackets ([]), and braces () to indicate an implied continuation line. The term "implicit" refers to the use of a statement that spans numerous lines without using the line continuation character ().
It is recommended to position the wrapped element vertically or to use hanging indentation when using implicit continuation lines. When describing parenthesized statements in Python, hanging indentation refers to the practice of indenting lines up until the closing parenthesis, with the opening parenthesis serving as the final non-whitespace character on each line.
Bad Practice
Arguments included in parentheses are not indented or vertically aligned.
# multi-line statement
def multiply(arg1, arg2, arg3,
arg4, arg5):
"""The function multiplies five numbers"""
return arg1 * arg2 * arg3 * arg4 * arg5
Pylint Output
Pylint recognizes the wrong indentation.
Good Practice
Arguments that are included in parentheses are vertically aligned.
# multi-line statement
def multiply(arg1, arg2, arg3,
arg4, arg5):
"""The function multiplies five numbers"""
return arg1 * arg2 * arg3 * arg4 * arg5
Arguments inside the parenthesis are aligned using hanging indentation.
# multi-line statement
def multiply(
arg1, arg2, arg3,
arg4, arg5):
"""The function multiplies five numbers"""
return arg1 * arg2 * arg3 * arg4 * arg5
2. Operators
Always add a single space to each side of the following operators:
Assignment (=)
* Augmented assignment (+=, -=, etc)
* Comparison operators (<, >, <=, >=, ==, !=)
* Logical operators (and, or, not)
* Membership operator(in, not in)
* Identity operator(is, not is)
Bad Practice
There are no spaces around the operators.
# pylint: disable=C0114
# Comparison and assignment operators
print(5<4)
# Membership operator
if 5in[1, 2, 3, 4, 5]:
print('is contained')
Else:
print('is not contained')
By adding a comment at the beginning of the code, as shown above (#pylint: disable=C0114), we may quickly disable a warning in Pylint.
The absence of a module docstring is indicated by the C0114 warning. A module, function, class, or method definition's opening statement is referred to as a docstring. All modules should include a docstring at the start that describes what the module does in accordance with PEP 257 (the handbook providing docstring norms).
In order to keep things simple, we won't put a docstring at the top of each module in this post. Writing documentation in practice, however, is strongly advised. The same as previously, I advise you to have a closer look at PEP 257 recommendations if you want to learn more about how to utilize docstrings in Python.
For the sake of simplicity, we will not include a docstring at the start of each module in this post. However, it is strongly advised to write doctrings in practice. As previously said, if you want to learn more about how to use doctrings in Python, I recommend you read the PEP 257 recommendations.
link:https://peps.python.org/pep-0257/
3. Whitespaces after a comma, semicolon, or colon (but not before!)
After a comma, semicolon, or colon, we must use whitespace. However, PEP 8 suggests avoiding them as soon before.
Bad practice
We can see the following improper practices in the code:
* There is no whitespace following the comma that separates each element of the list.
* There is no whitespace behind the colon (:) that separates each key-value pair in the dictionary.
* There is a whitespace exactly before the comma that separates each element of the tuple.
# list
numbers = [1,2,3,4,5]
# dictionary - students and grades
grades = {'Ramu':10, 'Anna':9.5, 'Pooja':3.5}
# tuple - latitude and longitude
coordinates = (48.137 , 11.576)
Pylint output
Pylint finds that a whitespace character is required after each comma that separates the list's items (numbers). Furthermore, pylint identifies a space before the comma in the tuple (coordinates). It does not, however, recognize the missing space after the colon that divides each key-value combination.
I'd like to point out that the PEP8 guidelines do not state that a whitespace is necessary after a comma, colon, or semicolon. However, it is a common practice that most style checkers identify as pylint.
Good Practice
There are spaces after each comma, colon, and semicolon, but not before.
# list
numbers = [1, 2, 3, 4, 5]
# dictionary - students and grades
grades = {'Ramu': 10, 'Anna': 9.5, 'Pooja': 3.5}
# tuple - latitude and longitude
coordinates = (48.137, 11.576)
4. Whitespaces between brackets, brackets or braces.
Within brackets, brackets or brackets, whitespaces are utilized.
# immediately inside parentheses, brackets or braces.
# after parentheses
print( 'Hello world' )
# after brackets
numbers = [ 1, 2, 3, 4, 5 ]
# after braces
cities = { 'Mumbai', 'Goa', 'Chennai' }
Pylint Output
Pylint detects that there are no spaces before or after brackets, brackets, or braces.
Good Practise
There are no whitespaces directly inside brackets, brackets, or braces.
# immediately inside parentheses, brackets or braces.
# after parentheses
print('Hello world')
# after brackets
numbers = [1, 2, 3, 4, 5]
# after braces
cities = {'Mumbai', 'Goa', 'Chennai'}
5. Arguments for keywords and defaults
Keyword arguments
An argument is a value that is passed to a function when it is called. Python functions take two kinds of parameters: positional arguments and keyword arguments. When utilizing positional arguments in order to call a function, the parameters must be provided in the correct sequence. On the contrary, because the keyword arguments are preceded by an identifier (keyword=value), they can be supplied in any sequence. When invoking a function with keyword parameters, we do not need to put spaces around the = sign, according to PEP8.
Default arguments
Default values can be assigned to function parameters. If no argument is given when calling a function, the default value is used. When defining default parameters, no spaces are used around the = sign, as previously.
Bad Practice
When applying keyword and default parameters, spaces are used around the = symbol.
# pylint: disable=C0114
# keyword and default arguments
# function that calculates cylinder volume
def cylinder_volume(height, radius = 5.0):
"""Volume of a cylinder
Parameters:
height (float): Height of the cylinder
radius (float): Radius of the cylinder (default 5.0)
Returns:
float:Volume of the cylinder
"""
return height * 3.14159 * radius**2
# calling the function using positional arguments
cylinder_volume(10.3, 7.2)
# calling the function using keyword arguments
cylinder_volume(height = 10.3, radius = 7.2)
Pylint output
Spaces are not permitted around keyword arguments, according to Pylint.
Good practice
There are no spaces surrounding the = symbol.
# pylint: disable=C0114
# keyword and default arguments
# function that calculates cylinder volume
def cylinder_volume(height, radius=5.0):
"""Volume of a cylinder
Parameters:
height (float): Height of the cylinder
radius (float): Radius of the cylinder (default 5.0)
Returns:
float:Volume of the cylinder
"""
return height * 3.14159 * radius**2
# calling the function using positional arguments
cylinder_volume(10.3, 7.2)
# calling the function using keyword arguments
cylinder_volume(height=10.3, radius=7.2)
6. Catching exceptions
Python error messages are classified into two types: syntax errors and exceptions. Syntax errors arise when Python is unable to comprehend the code, signaling that something is incorrect with the program's syntax. Exceptions, on the other hand, arise when unexpected things happen during code execution.
Python displays a notice in the following block code showing that we encountered a TypeError when attempting to convert a text to a hexadecimal number because the hex function requires an integer as input.
# an exception is raised when trying to convert a string to a hexadecimal number
hex('1')
# TypeError: 'str' object cannot be interpreted as an integer
In Python, we use the try and except statements to manage exceptions. The operation that potentially causes an exception is contained in the try clause and the code that handles the exception is contained in the except clause. The try clause is executed one statement at a time. If an exception occurs, the try statement is terminated, and the except statement is performed.
Bad practice
When an exception occurs during the execution of the try block, the code in the except block is performed. According to PEP8 standards, using bare unless clauses is not advised since they capture all exceptions, including SystemExit and KeyboardInterrupt exceptions, making it more difficult to terminate a programme with Control-C.
# pylint: disable=C0114
# exceptions
def int2hex(integer):
"""Function that converts an integer to a hexadecimal string"""
try:
hexadecimal = hex(integer)
return hexadecimal
except:
print('An integer should be provided as input')
# call the function
int2hex(5)
Pylint output
Pylint detects the use of a simple unless clause.
Good practice
In the except block, we should define the error we wish to handle:
# pylint: disable=C0114
# exceptions
def int2hex(integer):
"""Function that converts an integer to a hexadecimal string"""
try:
hexadecimal = hex(integer)
return hexadecimal
except TypeError:
print('An integer should be provided as input')
# call the function
int2hex(5)
The programme now captures the TypeError error but not the others.
7. Boolean variables
Bad practice
It is not permissible to use == or is to compare boolean variables to True or False in if statements.
# pylint: disable=C0114
# boolean
is_raining = True
if is_raining == True:
print('Take an umbrella')
else:
print('You do not need an umbrella')
Pylint output
Pylint recognizes that the comparison to True should just be the expression (is_raining).
Good practice
In the if statement, we have to use the boolean variable directly as follows:
# pylint: disable=C0114
# boolean
is_raining = True
if is_raining:
print('Take an umbrella')
else:
print('You do not need an umbrella')
8. Check for prefixes and suffixes
Bad practice
Using string slicing to check for prefixes or suffixes is a bad practice.
# pylint: disable=C0103
# pylint: disable=C0114
# check for prefixes and suffixes
name = 'Meena'
if name[0] == 'M':
print('Name starts with M')
if name[-3:] == 'ing':
print('Name ends with ing')
Pylint output
pylint, however, does not identify this style issue, despite the fact that it is mentioned in the PEP 8 guide.
Good practice
To check for prefixes or suffixes, we must use str.startswith() and str.endswith() according to the PEP8 style rule.
# pylint: disable=C0103
# pylint: disable=C0114
# check for prefixes and suffixes
name = 'Meena'
if name.startswith('M'):
print('Name starts with M')
if name.endswith('ing')':
print('Name ends with ing')
str.startswith(prefix, start, end) determines if a string begins with a given prefix. If the string begins with the provided prefix, the method returns True; otherwise, it returns False. Start and end are optional parameters that define where the check starts and concludes.
If the string (str) finishes with a provided suffix, the str.endswith(suffix, start, end) method returns True. If it does not, it returns False. The function, like previously, contains two optional parameters start and end, which indicate the indices from which the test begins and finishes.
9. Imports
A module is a Python code file. When working on large projects, it makes logical to organize your code into numerous files and import them into other Python files as needed. To import a Python module, just write import followed by the filename. In addition to importing our own modules, we may import built-in modules from the Python standard library and third-party libraries. Imports are written at the top of the Python script, one at a time, according to PEP8 rules. Furthermore, imports should be organized in the following order:
Imports from a standard library
Third-party imports
Imports of local scripts
Bad practice
Imports are not written in separate lines and are not organized correctly.
# pylint: disable=C0103
# pylint: disable=C0114
import pandas, matplotlib
import csv
Pylint output
Pylint notices that the modules pandas and matplotlib are not separated by lines. Furthermore, Pylint notices that the Python standard library module (csv) should come before the third-party imports (pandas and matplotlib). Pylint recognizes that all imports are unneeded, as shown below, because we did not add any further code for simplicity.
Good practice
Imports are put on distinct lines and sorted alphabetically.
# pylint: disable=C0103
# pylint: disable=C0114
import csv
import pandas
import matplotlib
10. Lambda expressions
These functions can take several parameters and return the value specified by the expression. Lambda functions are not just found in Python; they are also supported by other programming languages.
It is not encouraged by PEP8 standards to assign a lambda function directly to an identifier. It is best to utilize ordinary functions (defined with the def keyword) in this scenario.
Bad practice
A lambda expression is applied to an identifier directly (multiply).
# pylint: disable=C0103
# pylint: disable=C0114
# lambda functions
# define the function
multiply = lambda x, y: x * y
# call the function
multiply(5, 7)
Good Practice
Using the def keyword, we define the function.
# pylint: disable=C0103
# pylint: disable=C0114
# lambda functions
def multiply(x, y):
"""The function multiplies two numbers"""
return x*y
# call the function
multiply(5, 7)
Pylint is not perfect, but it may significantly enhance the quality of your code! Remember that code is read far more frequently than it is created, which is why it is critical to follow standards that will assist others in understanding your code. A clear code also demonstrates professionalism. That's why I recommend running your code via quality checkers like Pylint (there are lots more!). It will appear more clear and professional.