10 common mistakes Python programmers make (and how to fix them)
Tags: python, beginners, codenewbie, todayilearned
Published: 2021-11-02 18:42:24

Python is one of the most popular programming languages in the world. It is an object-oriented, high-level programming language. Python is known for its code readability, minimalism, and efficiency.

Developers have a lot of flexibility when writing Python code because it is an interpreted language. But with great freedom comes great responsibility, and even greater room for error. In this article, we’ll review 10 common mistakes Python programmers make and how to fix them.

**We’ll cover**:
* [Incorrect indentation](#IncorrectIndentation) 
* [ImportError caused by identical module names](#ImportErrorCausedByIdenticalModuleNames)
* [Mutable default arguments](#MutableDefaultArguments) 
* [Forgetting a colon at the end of structural sentences](#ForgettingAColonAtTheEndOfStructuralSentences) 
* [Inconsistency with parentheses or brackets](#InconsistencyWithParenthesesOrBrackets) 
* [Incorrect application of the init function](#IncorrectApplicationInit)
* [Modifying class variable values without using the class name](#ModifyingClassVariableValuesWithoutUsingClassName) 
* [Misunderstanding Python scope rules](#MisunderstandingPythonScopeRules) 
* [Misunderstanding how Python binds variables in closures](#MisunderstandingHowPythonBindsVariablesInClosures) 
* [Exhausting iterators](#ExhaustingIterators) 
* [Wrapping up and next steps](#nextsteps)

<br>

<a name="IncorrectIndentation"></a>
## Incorrect indentation
In Python, indentation indicates whether a given line of code belongs to a preceding statement in a block of code. Python’s official style guidelines (PEP 8) recommend an indentation of four spaces. However, you have the flexibility to choose any amount of spaces, as well as the tab key. Whichever indentation you prefer, Python asks for one thing: **consistency.** 

To fix an indentation error, try to:

- **Commit to either spaces or tab keys, but don't combine them**: You'll want to be extra attentive as you may not realize you’ve combined tabs and spaces if the resulting indentation looks identical. You might try using a Python IDE or code editor that offers a search/replace tool to replace tabs with spaces, or vice versa. 
- **Stay consistent with the number of spaces you use, if using spaces**: You don't have to use PEP 8’s recommendation of four spaces, but whichever number you choose, stick with it!

<br>

<a name="ImportErrorCausedByIdenticalModuleNames"></a>
## ImportError caused by identical module names
You might be importing a built-in module that you are certain is in Python's Standard Library, but to your surprise, the interpreter returns "ImportError." This issue commonly arises when developers import a file into their library that shares an identical name with one of the Standard Library's built-in modules. In these situations, **Python prioritizes the identically-named module that you’ve installed over the Standard Library's built-in module.** The solution? Simply rename the file in your library to a unique name that is not shared with a Standard Library module.

<br>

<a name="MutableDefaultArguments"></a>
## Using mutable default arguments
Another common problem arises when assigning mutable data types for default arguments. **Python only evaluates defaults for mutable data types once**, at the time that you create the function. It will not initialize the default for any subsequent function execution. You may not notice anything unusual if you only perform one function call in your code, but if you call on it a second time, Python will use the default evaluated at the time of the first function call. 

Let’s say you’re using a mutable data type such as a list. The interpreter will use the same list that was created when the function was initially defined. This code reveals the perplexing behavior that can occur when you append that list.

```
def some_func(default_arg=[]):
    default_arg.append("some_string")
    return default_arg
 
print(some_func())
print(some_func())
print(some_func([]))
print(some_func())

=>['some_string']
=>['some_string', 'some_string']
=>['some_string']
=>['some_string', 'some_string', 'some_string']
```

One way to resolve this programming error is **to assign "None" as the default value.** You can later check that any value passed to the function corresponds to the argument.

```
def some_func(default_arg=None):
    if default_arg is None:
        default_arg = []
    default_arg.append("some_string")
    return default_arg
 
print(some_func())
print(some_func())
print(some_func([]))
print(some_func())
=>['some_string']
=>['some_string']
=>['some_string']
=>['some_string']
```

<br>
 
<a name="ForgettingAColonAtTheEndOfStructuralSentences"></a>
## Forgetting a colon at the end of structural sentences
You may have forgotten a colon at the end of a sentence if you're receiving syntax errors. In Python code, each structural sentence ends with a colon. This also applies to function headers, where the colon triggers indentation for subsequent lines within the function. This is a common programming error for beginning Python developers. To fix it, just drill this rule until it becomes second nature: **every structural sentence ends with a colon!**

<br>

<a name="InconsistencyWithParenthesesOrBrackets"></a>
## Inconsistency with parentheses or brackets
This is a surprisingly common mistake for beginner Python developers. As with math, **the amount of open and closed parentheses and brackets must match** for a statement to be readable. Make sure to review your code and ensure that each open parentheses or bracket has a corresponding closed one to finish its thought.

<br>

<a name="IncorrectApplicationInit"></a>
## Incorrect application of the init function
Used as a constructor, the init function creates an object or allocates memory to a new class object. The init function is used as a constructor when an object is created from a class and allows the class to initialize the class’s attributes. It is, in other words, exclusively used to set values. However, a common mistake Python developers make is to attempt to use this method to return values. To fix this, just understand that **the init method’s function in Python is exclusively for use as a constructor.**

<br>

<a name="ModifyingClassVariableValuesWithoutUsingClassName"></a>
## Modifying class variable values without using the class name
As an object-oriented language, class variables and instance variables operate differently in Python. Unlike instance variables, class variables are shared by instances across the entire class. If you modify a class variable without using the class name, an instance variable is created that holds the same name as the class variable. New instance variables with the same names as class variables actually shadow the class variable, so if you are attempting to modify a class variable a second time, the objects in the code will be referencing the instance variables instead. To fix this mistake, be sure to **use the class name when modifying the class variable** so that all objects also receive the newer value. 

<br>

<a name="MisunderstandingPythonScopeRules"></a>
## Misunderstanding Python scope rules
You may have misunderstood Python's scope rules if your code returns "UnboundLocalError." This is a common mistake that Python developers encounter when using lists. 

Python’s unique scope analysis is based on the LEGB rule, or Local, Enclosing, Global, Built-in rule. **Following the order of the LEGB rule, Python will first assume that any variable assigned in a range is *local* to that scope**, and will override any variable with the same name in an external scope. This means a code that ran as expected when you assigned the variable might later return an "UnboundLocalError" when you call the function again. 

In this example, we start with code that runs as expected.

```
x = 10
def bar():
    print(x)
bar()
=>10
```

This code returns "UnboundLocalError" when we assign a variable. Per the order of the LEGB rule, Python recognizes this new value assignment for "x" as a local variable rather than an outer scope variable.

```
x = 10
def foo():
    print(x)
    x += 1
foo()
=>Traceback (most recent call last):
=>  File "main.py", line 5, in <module>
=>    foo()
=>  File "main.py", line 3, in foo
=>    print(x)
=>UnboundLocalError: local variable 'x' referenced before assignment
```

To fix "UnboundLocalError," you just need to add an assignment statement to explicitly declare that the variable is global.

```
x = 10
def foobar():
    global x
    print(x)
    x += 1
foobar()
=>10
```

<br>

<a name="MisunderstandingHowPythonBindsVariablesInClosures"></a>
## Misunderstanding how Python binds variables in closures
Python late binds its variables in closures. This means that it calls upon the values of the variables that were returned the first time the affected function was called. This may not always be problematic for your code’s purposes, but if it is, you can fix this by creating a closure that will bind immediately to its arguments, such as a default argument.

<br>

<a name="ExhaustingIterators"></a>
## Exhausting iterators
It is important for early Python developers to understand that **both iterators and generators can be exhausted**. Developers who are transitioning from Python2 to Python3 might especially encounter this issue. Python3 has a lot more generators, which helps make it more efficient. However, this can be difficult for those facing new frustrations they didn’t have in Python2. 

This type of mistake may look like this: you may invoke an iterator function in Python2, such as zip, to combine two lists, and try to print out this list later on. Now, if you use this same exact code in Python3, it will not return all the values to you at once. This is because you have exhausted the iterator and it has no remaining values to return. 

You can fix this by simply converting a function to a list from the get-go. While you can exhaust an iterator, you can’t exhaust a list. 


<br>

<br>
<a name="nextsteps"></a>

## Wrapping up and next steps
We commend you for committing the time and grit to be a well-rounded Python developer! With applications ranging from machine learning to data science, Python isn't going away anytime soon. 

To keep your learning going, Educative offers several interactive online Python classes. 

You might want to check out one of these courses:
 
[**Full Speed Python**](https://www.educative.io/courses/full-speed-python), which covers fundamentals such as:
- Modules and functions 
- Iterations and loops
- List comprehension

[**Python FTW: Under the Hood**](https://www.educative.io/courses/python-ftw-under-the-hood), which covers common Python quirks and mistakes, such as:
- Modifying dictionaries while iterating over them
- Misuse of the bool function
- Issues printing tuples

*Happy learning!*
<br>
<br>
 
### Continue learning about Python
* [Learn Python 3: Python basics in 7 coding challenges](https://www.educative.io/blog/python3-coding-challenges)
* [Python tutorial: Get started with Jupyter Notebook](https://www.educative.io/blog/python-jupyter-notebook)
* [A complete guide to web development in Python](https://www.educative.io/blog/web-development-in-python)