In this post, we will cover some best practices that will help you write good-quality Python code. By this, I mean code that:
- It is easy to read
- Clear in its intent
- Aligns to the Python naming standards
Let's dive in …
Naming - Casing and Grammar
When writing Python code, following naming conventions for variables, functions, and classes is good practice. Here are the general rules for naming conventions in Python:
Casing | Grammar | Example | |
Classes | CamelCase | Noun | Router
|
Variables (and attributes) | lowercase with underscores | Noun | vlan_name
|
Functions (and methods) | lowercase with underscores | Verb | get_vlan()
|
Here are the examples shown a little differently:
# Class
# - Noun
# - CamelCase
class Router(object):
...
# Variables
# - Noun
# - lowercase /w underscores
vlan_name = "VLAN_100"
# Methods/functions
# - Verb
# - lowercase /w underscores
def get_vlan_id():
....
Naming - Type and Length
When naming within Python, consider the type and length of what you are naming. The name should represent its scope and have some reference to its value. For example:
vlan_ids = [100, 200, 300]
# what does x relate to?
for x in vlan_ids:
print(x)
# name length is too long for its scope
for the_vlan_id_in_loop in vlan_ids:
print(the_vlan_id_in_loop)
A much better example of naming is:
# short name for scope (as it's only used within the loop),
# and v relates to vlan_id
for v in vlan_ids:
print(v)
Furthermore, if we need to ignore a variable, we can ignore the value and declare that it is not required by assigning it against _
like so:
vlan_100, _, vlan_300 = [100, 200, 300]
Will this speed up your scripts or reduce the memory footprint? Not really, but it will clearly show your code's intent, making it easier to read and understand.
Functions
Functions should be small and only do one thing. For example. If we look at the following:
from netmiko import ConnectHandler
device = {
'device_type': 'cisco_ios',
'ip': '10.1.1.1',
'username': 'admin',
'password': 'admin',
}
def is_vlan_present(vlan_id):
with ConnectHandler(**device) as conn:
vlans = conn.send_command('show vlan', use_textfsm=True)
for vlan in vlans:
if vlan['vlan_id'] == vlan_id:
return True
return False
is_vlan_present(100)
# Output:
# True
We can see that is_vlan_present
is doing 2 things. Its fetching the data and then checks the data. A much better approach is to create 2 functions like so:
from netmiko import ConnectHandler
device = {
'device_type': 'cisco_ios',
'ip': '10.1.1.1',
'username': 'admin',
'password': 'admin',
}
def get_vlans():
with ConnectHandler(**device) as conn:
output = conn.send_command('show vlan', use_textfsm=True)
return output
def is_vlan_present(vlan_id):
vlans = get_vlans()
for vlan in vlans:
if vlan['vlan_id'] == vlan_id:
return True
return False
is_vlan_present(100)
# Output:
# True
Note: This is just an example to show the point; in the real world, this would be written much more effectively (i.e. not connecting to the device for each VLAN check).
Styling
When it comes to styling, i.e. indents, spacing etc., there are a few tools that you can use to automate the process of updating the styling of your Python scripts. The defacto tool that is currently used it, Black. Which is a general-purpose formatter.
Here's a quick example:
$ pip install black
$ cat my_script.py (BEFORE)
vlans = [
{"vlan_name": "SALES", "vlan_id": 100},
{"vlan_name": "HR", "vlan_id": 101},{"vlan_name": "FINANCE", "vlan_id": 102}
]
def is_vlan_present( vlan_id ):
for vlan in vlans:
if vlan_id in vlan.values():
return True
return False
$ black my_script.py
All done! ✨ 🍰 ✨
1 file reformatted.
$ cat my_script.py (AFTER)
vlans = [
{"vlan_name": "SALES", "vlan_id": 100},
{"vlan_name": "HR", "vlan_id": 101},
{"vlan_name": "FINANCE", "vlan_id": 102},
]
def is_vlan_present(vlan_id):
for vlan in vlans:
if vlan_id in vlan.values():
return True
return False
That's it for this post on Python coding best practices. I hope you found it helpful and happy coding!