The Good, The Bad and The Ugly - When coding in Python














I've been coding in Python in the last few years and I'm still learning about good practices to improve my code quality.  Throughout my journey I've seen good, bad and ugly practices.

Here's a list of common mistakes when developing in Python.


Using import *

This is a very inefficient practice. If you only need a module you should import that module only. It will help with debugging and readability.


| Bad


from typing import *


| Good


from typing import Dict, Optional


Using type() to compare types

isinstance is usually the preferred way to compare types. It is faster and it considers inheritance. 


name = 'Di Maria'

Bad


if type(name) == str:
   print('it is a Di Maria string') 


Good


if isinstance(name, str):
   print('It is a Di Maria string')
else:
   print('It is something else')  


Not following PEP8

PEP8 is a document everyone learning Python should read. It provides guidelines and best practices on how to write Python code. 
Fortunately some of the rules are incorporated to IDEs. 

For example, if you write code that don't follow PEP8 guidelines, your IDE (in this case, PyCharm) will show you some ugly underlines:



If you hover the underlines it will tell you how to fix it.


Using 'in' to check if an element is contained in a large list

Checking if an element is contained in a large list using the in statement might be slow for large lists. Consider using a set or a bisect instead.

Bad

list_of_players= ['Kylian', 'Leo', 'Alexis', 'Angel']

if 'Leo' in list_of_players:
   print('GOAT found') 


Good


set_of_players= {'Batigol', 'Leo', 'Enzo', 'Julian'}
if 'Leo' in set_of_players:
   print('GOAT found')  


Not using  get() to return default values from a dictionary

Usually, when working with dictionaries, you will need to check if a key exists in one of them. 

While using the in statement might be your first reaction, you should use the more concise  dict.get(key[, default]) built-in method from the Python Standard Library. 

If the key exists in the dictionary then the value for that key will be returned. If it doesn't then the value specified as the second argument in the get() will be returned.

Bad


bands =  {'GEN': 'Genesis', 'PNK': 'Pink Floyd', 'MSE': 'Muse'}
if 'GNR' in bands: 
  name = band['GNR']
else: 
  name = 'undefined' 

Good

bands =  {'GEN': 'Genesis', 'PNK': 'Pink Floyd', 'MSE': 'Muse'}
                                      
name = bands.get('GNR', 'undefined')


Do you spot the difference? A one liner solution, very pythonic 

Never using comprehensions (or using them all the time)

Comprehension offers a shorter syntax when you want to create a new sequence (list, dictionary, etc.) based on a sequence already created. 

In fact I have a previous post on this topic. 

Say you need to lower case a list of world champion players, you could do this in a loop:

Bad


world_champs =  ['MESSI', 'DIBU MARTINEZ', 'CUTI ROMERO']
lower_case = []

for champ in world_champs: 
  lower_case.append(champ.lower())


Or you can do this with a very sexy one-liner comprehension

Good

lower_case =  [champ.lower() for champ in world_champs]

Comprehensions are very useful, but don't overuse them! Remember the Zen of Python: "Simple is better than complex".


Not using a context manager when reading or writing files

This is a common anti pattern saw on different code reviews. Context managers in Python help facilitate proper handling of resources, to control what to do when objects are created or destroyed. This removes the overhead of handling the creation or deletion of objects. 
 

Bad

file_obj =  open('abc.txt', 'r')

data = file_obj.read()
file_obj.close()


Good

with open('abc.txt', 'r') as file_obj:

     data = file_obj.read()


Ignoring Comments

Undocumented code is a nightmare. These are the people who may complain about it:

  • You in 6 months when you'll forget why you wrote that line of code. 
  • any colleague of yours who take over the project. 
Code should always be clear in what it's doing and comments should clarify why you're doing it. At the same time, be concise when you comment your code. When your code is self-explanatory, comments are not needed. 


Not Updating Comments

Comments that contradict the code are worse than no comments at all. An outdated is misleading for everyone working on the code. 
There's always time to update comments. 


No exception type(s) specified

Not specifying an exception type night not only hide the error but also leads to losing information about the error itself. So it's better to handle the situation with the appropriate error rather than the generic exception.

Bad

try:
    5/0
except:
    print('Exception')


Good

try:
    5/0
except ZeroDivisionError as e:
    print(f'ZeroDivisionError: {e}')
except Exception as e: 
    print('Exception')
else:
    print('No errors')
finally:
    print('bye')


Over-engineering everything

You don't always need a class. Simple functions can be very useful. 


Using single letter variable names

Bad


l = [2, 3, 4, 5]


Good


numbers = [2, 3, 4, 5]

 

Formatting with the + operator

Probably one of the first things we learn in Python is how to join strings with the + operator. 
This is a useful yet inefficient way to join strings, the more strings you need the more  +  you'll use.

Bad


variable_a =  'value'
print('this is variable_a value:' + variable_a + '')

Good

variable_a =  'value'
print(f'this is variable_a value: {variable_a}'

The best part of of the the f-strings is that's not only useful for concatenation but has 



References: 

Comments

Popular Posts