5 Python Code Smells You Should Be Wary Of | by Anupam Chugh | Towards Data Science

Code smells are a widely used term to indicate warning signs in a codebase. Unlike, what it may seem, they aren’t a bug in the code that requires immediate attention.
Instead, code smells are characteristics that showcase a deeper problem or rather crack in your code that might lead to possibly bigger repercussions in the future if not rectified.
Code smells are a sign of weakness or design flaw that might cause readability, maintainability, and scalability issues. Typically it originates due to bad practices and not using the correct tools.
Python is one of the most popular languages and a lot of it has to do with its fairly easy learning curve and high-level pseudo-English like syntax. But you could easily get bogged down with a single way of doing things.
In the next few sections, we’ll look at a few quintessential Python code smell cases and how to avoid them. Let’s dig in.
Using default arguments is a fairly common exercise in Python wherein you can set predefined values in the function and opt to change it at call time. This is useful when setting literals, numbers, or booleans as it helps you prevent a long list of parameters with redundant values.
But setting mutable values as default arguments can be dangerous and lead to nasty bugs. Just consider the below example:
def addElements(a=[]):    a.append(5)    return aaddElements()# [5]addElements()# [5, 5]
As you can see the same function gives different results on every invocation. The problem with mutable defaults in Python is that they are evaluated once only when the function is defined.
Every time the function is called you’ll be using the mutated value which can cause unexpected issues as tracking the function calls becomes a headache.
Hence using None as the default value and assigning the mutable variable inside a function is safer bet as you won’t end with maintainability issues. Use mutable default arguments only if you’re sure you need them.
Python’s for loops aren’t the most idiomatic way of writing code but sometimes you need them. Now, for loops in Python behave differently from other languages and you could perhaps instinctively write them in non-idiomatic ways by using the traditional style of range(len()) as shown below:
names = ["a", "b", "c"]for i in range(len(names)):    print(i, names[i])
It’s fairly common to iterate over C-style index-based for loops like above but it’s a bad practice. Not only is it less pythonic since it forces you to access elements through an explicit index variable but there’s also a case of readability issues.
Using enumerator gives you the advantage of having a tuple that takes care of tracking the index value and element together. Besides being cleaner, it’s more optimized and also provides us with an optional second argument to set the starting count.
for i, name in enumerate(names):   print(i, name)
Firstly, lets put it straight: Loops aren’t bad. But when you’re applying transformations inside them, it can lead to long bloated conditional codes.
In such cases, it’s important to not ignore built-in functions like map() filter() and reduce() that are already at our disposal. More importantly, Python provides List comprehensions which is easily the most Pythonic way of replacing for loops.
Nested for loops is another quintessential case of code smell. Python programmers can easily fall prey for them when doing pattern matching or running more than one iterables together. The following code would start looking ugly once you add a few more lines in it:
for x in listA:    for y in listB:        r.append((x, y))
Using itertools not only gives a performance boost but is also flat and clean. Just look how clean the above code would look with itertools.product()
for x, y in itertools.product(listA, listB):         r.append((x, y))
By using product above, we can also pass it in other high order functions easily.
When iterating over multiple lists simultaneously, using the zip() function is a good idea too(alongwith enumerator if you need index).
List comprehensions are powerful and flexible ways of creating lists but can easily be misused. Or rather abused. Let’s look at some cases.
Often to try out the fancy stuff, we can start obsessing with list comprehensions and use it in areas where it isn’t really needed. For instance, we can use the list constructor for simple cases
names = ["A","B","C"][x.lower() for x in names]#use thislist(map(str.lower, names))
List comprehensions help to define and create lists easily but they’re stored in memory for the whole time. This could be detrimental with large data if you aren’t using the instance. Hence using generator expressions is a better alternative as it loads values on demand one at a time.
Nested Comprehensions are another cause of concern as it can cause readability issues and confuse your peers. It’s important to know when to use it and when to fall back onto for loops.
Booleans are the easiest data type to learn. And in Python providing us with named arguments makes life much easier.
But they can easily lead to complex code with nested if else blocks and readability issues. Multiple booleans are a cause of concern as they give rise to hidden dependencies.
It’s better to prefer enums instead of multi-boolean logic. Enum data types are extensible and ensure a better code structure.
Global variables are evil across all languages and Python is no different. While sometimes, we do comprise and use them in small doses but misusing them as a shortcut for passing or accessing data can be dangerous because they are mutable.
Hence keeping a track of its state becomes tricky as you never know who might change it. Namespace pollution due to naming conflict can also originate if you start using global variables everywhere.
We’ve all witnessed code smells and at times can be the reason for their existence as well. Mysterious comments, redundant string literals, and magic numbers are also a code smell.
While writing comments it’s important to address the “why” part since the “what” part should be self-explanatory from the code itself.
The key is to spot code smells soon and get rid of them quickly. That’s it for this one. Thanks for reading.