Programming is equally an art as it is science and logic. Hence, even though programming style or technique can vary from developer to developer, there are some ground rules that are usually followed across the industry. These rules are laid out with the view of making the programming experience more uniform across the different variety of developers. Of course, the logic developed by one developer to achieve a certain task might not match the logic developed by another for the same task. But, the “format” of writing code should be similar, just like the general format of writing a business email is fixed.
“Best Practice” of any programming language refers to the recommended way of writing a program, such that it is uniform across the globe and any developer other than the original author is able to easily understand and modify the program.
In the industry, more than one person is involved in the development of a certain project for any given company. In such a case, it is of utmost importance that every member of the current team or any future employee working on the same project is able to understand the flow of the program and what the previous developer has done.
Since development is a dynamic process, even if a project is completed, for the time being, some other developer might be assigned to make updates or add features to the same project at a later date. That person then has to read through and understand the code written by the previous author in order to modify it. Hence, to make it efficient and easier for developers to read and understand the program written by other developers, it is necessary to follow a certain set of recommended “Best Practices”.
There are a lot of best practices that are followed across the industry. Covering all of them is beyond the scope of this article. However, listed below are a few of the most common practices that any beginner developer should adhere to.
When a developer sees a piece of code, he/she should instantly be able to understand which name refers to what kind of data. A proper naming convention is thus important to allow the programmer to easily determine what is the purpose of a particular user-defined instance/variable.
The developers of Python Community found out that even when following some of the “Best Programming Practices” overall, the kind of Python code that many developers wrote vastly varied. This created inconveniences to the community and to anyone who was trying to read and/or modify existing code.
Hence, the community came up with a set of guidelines, which would layout a particular design flow that developers can adhere to in order to keep the code looking as uniform as possible. This set of guidelines, specifically designed for Python, have been mentioned in PEP 8 of Python’s official Documentation.
It is an extensive list of rules and Dos and Don’ts, most of which are beyond the scope of this article. Here, we have listed out a few of the commonly used PEP 8 style conventions.
# Aligned with opening delimiter. foo = long_function_name(var_one, var_two, var_three, var_four) # Add 4 spaces (an extra level of indentation) to distinguish arguments from the rest. def long_function_name( var_one, var_two, var_three, var_four): print(var_one) # Hanging indents should add a level. foo = long_function_name( var_one, var_two, var_three, var_four)
# Arguments on first line forbidden when not using vertical alignment. foo = long_function_name(var_one, var_two, var_three, var_four) # Further indentation required as indentation is not distinguishable. def long_function_name( var_one, var_two, var_three, var_four): print(var_one)
import numpy import pandas import matplotlib
import numpy, pandas, matplotlib
The above mentioned few guidelines just start to scratch the surface of the entire PEP 8 guideline lists. Again, it should be remembered that following PEP 8 guidelines is not mandatory, but is necessary.
Let us discuss modular and non-repetitive coding practices. Modular code means to write code in a short and understandable format. This improves code readability and reduces code repetition. Both of these can be achieved quite easily by the proper and frequent use of Classes and Functions.
We have already seen the use of classes in OOP practices. Below is an example of the usage of Functions.
# Code example of Python Python Function def find_odd_nums(num_list): return [num for num in num_list if num % 2 != 0] random_numbers = [2, 3, 5, 8, 34, 62, 6, 3, 4, 23, 13, 34, 0, 39] print(find_odd_nums(random_numbers))
[3, 5, 3, 23, 13, 39]
In the above example, we can see that the function is defined once and has been called later in the code. In this particular case, it is being used only once and hence is not as effective. But, often a particular operation needs to be done multiple times in the same program. At such times, it is recommended to define one function about it and then call it as many times as required.
This has many advantages. They are listed as follows:
Using functions in code is not mandatory, but is highly recommended. It is one of the most fundamental “good” programming practices.
Let’s start with what is Object-Oriented Programming, or more popularly known as OOPs. It is a way of programming which involves “objects” and the data associated with them. It is different in approach when compared to conventional procedure-oriented programming in the part that the primary focus is based on “objects” instead of “actions”. This might sound pretty insignificant, but it does provide a lot of advantages in terms of programming methodology, data management, data hiding, code modularity and so on.
Object-Oriented Programming is implemented with the use of Classes and its methods. Let’s try to understand this in brief. One class can have multiple “objects” or “instances”. Methods are class functions which are accessible by an “Object” or “Instance” of that class.
# Code example of Python Class # Create a class class Employee: def __init__(self, name, age, designation): self.name = name self.age = age self.designation = designation print("Employee created!") def modify_details(self, name, age, designation): self.name = name self.age = age self.designation = designation print("Employee details modified!") def show_employee(self): print("Name:", self.name) print("Age:", self.age) print("Designation:", self.designation) # Create two instances of a class employee_1 = Employee('John Doe', 35, 'Data Scientist') employee_2 = Employee('Natasha', 29, 'DevOps') # Show the details of the employees employee_1.show_employee() employee_2.show_employee() # Modify the details of employee 1. employee_1.modify_details('John Doe', 31, 'Data Scientist') # Employee 2 details remain unchanged, but employee 1 has been modified employee_1.show_employee() employee_2.show_employee()
Employee created! Employee created! Name: John Doe Age: 35 Designation: Data Scientist Name: Natasha Age: 29 Designation: DevOps Employee details modified! Name: John Doe Age: 31 Designation: Data Scientist Name: Natasha Age: 29 Designation: DevOps
Let’s answer the question by explaining the above code snippet.
As shown, a class is created which contains a few methods (functions) inside it. Note that we define the class only once, but we are creating two different instances (objects) of it, named “employee_1” and “employee_2”. These two employees are initialized with their relevant details, but they each are completely independent of each other. Even when the details of employee_1 is modified, employee_2 remains unaffected.
This was just a sneak peek into the advantage of using classes. In order to implement the above code without OOPs would mean to have a separate set of variables to store the details of each employee. This makes it very difficult to work with a huge number of variables as the number of employees increase.
Now that we know what can be the consequence of not using OOPs, let’s list out the advantages of using OOPs:
Hence, using OOP concepts in Python is highly recommended when working on larger projects.
Comments are parts of the code which are not executed by the interpreter. These are statements that are ignored during the execution of the program. Declaring comments are different for each programming language. In Python, a line starting with Hash ( # ) is considered a comment. The main purpose of comments is to convey information to the reader in a simpler way compared to the program itself. Commenting is of different types:
# Define a List of Brands brands = ['Apple', 'Google', 'Netflix', 'Amazon', 'Ford']
# Welcome to Python Programming # This is a dynamically typed object-oriented language # Let’s define some brand names brands = ['Apple', 'Google', 'Netflix', 'Amazon', 'Ford']
def find_odd_nums(num_list): """ This function is used to find and list out the odd numbers from a given list It takes a list of numbers as argument It returns a list of odd numbers """ return [num for num in num_list if num % 2 != 0]
As we can see from the above code snippets, Single-line comments are used to explain short segments of a program, whereas a multi-line comment is more descriptive about a bigger segment of code. Finally, DocStrings are used in Class and Function definitions. It explains the overall functionality of the Class/Function and explains the parameters and return statements.
Documentation may seem unnecessary, but it plays a key role in programming. A well-documented program is easier to read and understand when compared to an undocumented one.
Reading the program line-by-line with the view to understand/modify the program is tedious. In the industry, the length of programs often reach thousands of lines. In such a case, if at a later date any developer (even the author himself) tries to debug/modify the code, it becomes a challenge to understand the program since it is often difficult to understand the intended functionality of every part of the code. Here comes the necessity of proper documentation. A well-documented program is easy to read and understand and hence easier to debug when compared to an undocumented one.
Efficient Code Documentation:
Documentation does not mean to write what each line does. For Example:
# Print about Python print('Python is an Object Oriented Language')
Instead, Documentation is supposed to give insight to a bigger picture as to WHY a certain piece of code is written. For Example:
# create numpy array of zeros same as df_log to initialize the output output_predict = np.zeros((df_log.shape + future_day, df_log.shape))
In any development task, extensive documentation methods are followed by all companies. Good documentation practices are recommended for any programming language and are not limited to Python. Even though documentation is not mandatory, it is necessary.
Python is a language which has multiple versions (Python 3 and Python 2) and relies heavily on numerous libraries. Most programs utilize more than one library at a time, and each library has specific version requirements and interdependence with others. Moreover, a certain task may require the usage of one library which may not be compatible with other pre-existing libraries. Additionally, the developer may require more than one version of Python to be installed on the same machine at a given time. For example, Tensorflow, a Deep Learning library does not support Python 3.7 (at the time of writing this article). It supports up to Python 3.6. Yet, at the same time, the developer might want to use the newer features of Python 3.7 for other projects.
It is not practically feasible to install and uninstall the interpreter and all the associated libraries repeatedly every time a different version is required. Hence, Virtual Environments were introduced. These are self-contained environments which are completely independent of the system interpreter. One Virtual Environment can run Python 2.7 and another Virtual Environment can run Python 3.6 with Tensorflow in the same machine at the same time, while both of them are completely independent of each other. This is the main reason why Virtual Environments are used.
There are two methods to set up a virtual environment. One is by using the package “virtualenv” which can be installed via pip. The other method is to use “venv” that comes with Anaconda. Since Anaconda is not our primary focus here, we will stick to the default method.
The following tutorial is based on the assumption that the user has installed Python successfully and has access to pip (automatically gets installed with Python). If not, please refer to this guide on how to do so.
Now the virtual environment is set up, and it can be used as its own independent interpreter. No changes made here will affect the system interpreter.
Since Python heavily depends on external libraries and packages, more often than not, the programs that a developer creates require a certain set of libraries with their specific versions mentioned. One program might be dependent on numerous libraries. Hence, if any other developer wants to run the same program on his local machine, he/she would have to install the libraries manually, which is very tedious.
Hence, “requirements.txt” is a text file which contains the list of all the libraries required to run the particular program successfully. A sample file has been shown below.
The main advantage is that the developer can just use the command: pip install requirements.txt and this will install all the required dependencies automatically. This makes the process more streamlined.
Adding a “requirements.txt” file for any complex project is highly recommended since it has two main functions:
Hence, it is considered a “best practice” in Python development.
Generating “requirements.txt” manually is not a necessity. It can easily be generated by using the command: pip freeze > requirements.txt, which creates a list of dependent libraries as shown in the screenshot in a previous section.
However, this command generates the list for all the libraries currently installed in the environment from which pip is running from and hence will list all of them in the requirements.txt. This is unnecessary since the project might not require all the libraries. This is where virtual environments become a necessity, where each virtual environment is specifically set up for each project, and hence it contains a limited number of libraries required for that project. Creating the requirements.txt file from that active environment generates a specific list of necessary libraries leaving out the ones that are not used for the project.
Thus, so far we have discussed some of the most common “best practices” in python that are recommended across the development industry. This list, even though it does not include every “best practice”, lists the most important ones, and hence is a good place to start with. As the developer gets more experienced in programming, he/she will automatically come into terms with most of the “best practices”.