Common Python Programming Mistakes to Avoid

According to you, what are the most common Python programming mistakes that programmers may commit while coding? Well, there are some fundamental coding mistakes that some of us get used to of doing. Here, we’ll guide you how to spot these and improve your code quality. But it’s fair to understand the backdrop of these problems. 

Surprisingly, studies reveal that most of these mistakes happen to be the side effects of common misconceptions carried from the past programming experiences. For example, while learning to code, it goes without realizing that you made a few mistakes. Sometimes, you aren’t aware that you’re committing errors and leaving gaps in your programs.

It also reflects that you are taking it easy. Whereas learning to program is a tough task, everyone will accept who’s done it.

But the good part is that you can learn from the mistakes. You can take them as opportunities to grow. So, you shouldn’t be ashamed if you made one. In fact, each mistake leaves an important lesson to learn that you carry till you become an expert. And a good programmer never runs away, instead accept them as milestones in his path to development.

Nonetheless, in this article, we’ve laid down a few of the Python programming mistakes and tried to give a solution for each of them.

To begin with, you can go through the following checklist to help you avoid basic Python programming mistakes. It lists some of the key elements of a program/application and lays down a few points for improvements.

  • Identifiers: Make sure all your identifiers are meaningful. Avoid single letter and names like temp/flag.
  • Modularization: Split up logic using functions and classes. Don’t reinvent a library routine.
  • Formatting: Careful while indenting your code in Python. Use spaces instead of tabs and follow a consistent indentation pattern.
  • Comment Vs. Docstring: Python supports Docstring. It’s more convenient than using traditional comments. Make sure all your functions have a Docstring.
  • Code Analysis: Run a tool like PyLint on your code. It helps catch low hanging fruits like undefined vars, basic typos, un-used code, etc.
  • Unit Tests: Don’t miss to unit test your code. Use test modules like <unittest> or <unittest.mock> and deliver a dev tested code.
  • Code Profiling: Never guess the unknown, instead find them. Add modules like <timeit> or <cprofile> to locate hidden issues in your code.

If you are a keen learner and wish to excel in Python programming, then do follow the below two posts as well.

Now, check out the TOC (table of content) to flip through the list of common Python programming mistakes.

1. Ignorant of Python Scoping Rules (LEGB).
1.2 LEGB Rules.
1.1. LEGB Example.
2. Misconceive Identity as Equality.
3. Irrational use of Anti-patterns in your code.
3.1. Use of Java styled getter and setter functions.
3.2. Irregular use of Spaces with Tabs.
3.3. Underutilization of Python’s exception block.
3.4. Return inconsistent type values from functions.
3.5. Incorrect type checking.
4. Imports leading to circular dependency.
5. Misusing the <__init__> method.

Let’s now review the common mistakes and the actions you should take to fix them.

Common Python Programming Mistakes to Avoid.

1. Ignorant of Python Scoping Rules (LEGB).

If you aren’t aware of Python scoping rules, then there is a high probability of you making mistakes. It’s because Python uses a little different approach for scoping variables than other programming languages. For example, it allows accessing the variables declared inside loops or if statements from outside. It could be a bit confusing for someone coming from a C/C++ background.

Here is a sneak overview of the Python Scoping Rules a.k.a. LEGB.

  • L – stands for Local. It encompasses (identifier/variable) names specified within a function (using def or lambda) and not declared using the global keyword.
  • E – stands for Enclosing function locals. It includes a name from the local scope of any/all enclosing functions (for example, using def or lambda).
  • G – refers to Global entities. It includes names operating at the top-level of a module file or defined using the global keyword.
  • B – refers to Built-ins. It spans names that are preassigned as built-in names such as print, input, open, etc.

The LEGB rule specifies the following order for namespaces, meant to be used for searching the names.

Local -> Enclosed -> Global -> Built-in.

Python Programming Mistakes - Ignoring LEGB Rules

Python LEGB Rules At a Glance.

So, if a particular <name->object> mapping isn’t available in the local namespaces, it’ll then be looked up in the enclosed scope. If it doesn’t succeed, then Python will move on to the global namespace, and carry on searching the Built-ins. If it fails to find the name in any namespace, then a NameError will get raised.

To understand LEGB rules in detail, consider the below example. It showcases the practical usage and impact of the Python scoping rules. In this example, we’ve used four functions to demonstrate the application of scoping rules in Python.

LEGB Example.

1. Function: <access_local()> – It uses a local variable named as “token” (which also exist in global namespace) and initializes it with some value. Then, it queries the local and global namespaces to confirm its presence in both of them. And finally, print the “token” variable to make sure that it isn’t referencing the global variable.

2. Function: <access_enclosed()> – It has a for loop and initializing the token variable inside the loop. Then, it checks the global namespace that it includes the token variable too. Next, it prints the value of the token variable, which is the value set in the enclosed for-loop. It proves that variables defined in enclosed scope have a higher precedence than the global variables.Function: <access_global()>

3. Function: <access_global()> – In this function, first, we are confirming the presence of token variable in global namespace. And then, printing its value which remains same as we’d set at the onset i.e. at the global level.

4. Function: <id()> – Here, we’ve created our own definition of the built-in “id()” function. And as per the LEGB rules, built-ins have the least precedence. So whenever we call the “id()” function, Python will refer the one available in the global namespace.

5- NameError – As said above, the use of an undefined variable throws the NameError. You can see that happening with the last statement of the below code. In that line, we tried to print “token1” which resulted in the error.

Sample Code.

Here is the output of the above Python code. To interpret the below result, please refer the description given in the example.


2. Misconceive Identity as Equality.

Another common mistake that Python programmers commit is by mistaking <is> for <equality> while comparing integers. Since Python uses to cache integers, so they may not get aware of this error.

To grasp this concept, let’s consider the following two examples.


In the first example below, we’ve used two variables named as <sum> and <add>. And each of them stores the sum of two integers. Then, we are comparing the two variables with equality (==) operator. It’ll return true as both the variables hold the same value. Next, we are testing them using the identity (“is”) operator, but that too returns true. The reason is Python allocated the same address for both of them. You can confirm it from their id values printed at the end.

But the programmer didn’t realize how come the two distinct operations (“==” and “is”) yield the same result. And did the mistake unknowingly.

However, it’s going to cost him in the next example.


In this example, we’ve considered long integers to use. The catch here is that Python only caches integer between -5 to 256. Whereas the large numbers do occupy their separate boxes to sleep.

Hence, while matching large integers with the identity (“is”) operator wouldn’t yield the same result as you saw in the previous example.

The takeaway here is that the programmers should pay attention to the concept first before making blind use of any constructs.

However, you can read more on how Python deals with integers and voice any doubts in the comment box.


3. Irrational use of Anti-patterns in your code.

In general, an anti-pattern is a design approach to a commonly occurring problem that tentatively solves it but with possible side-effects.

Here, we are discussing a few of Python anti-patterns that programmers may tend to use while coding.

3.1. Use of Java styled getter and setter functions.

It’s often in Java termed as a best practice to define get/set functions for accessing members of a class. And you can see this pattern being applied in applications using Java Hibernate Framework.

On the contrary, such a use of functions in Python leads to extra code with no real benefit.

Anti-pattern example: Implement a Python class in Java-style.

What’s best for Java does not eventually be the same for Python. So if you are from Java background, you must cautiously think the way things work in Python.

Approach-1: How should you do it in Python.

In Python, it’s alright to access or manipulate a class member directly. And, usually, the use of protected or privates is scarce in Python. The members in Python are also public by default until you prefix them using <_> or <__>. This way, you can just emulate them to behave like protected (with _) or private (with __). Python obfuscates the names of variables starting with the <_> or <__> prefix to alienate them from the code outside the class.

You should see the code below after we removed the get/set functions.

Approach-2: Use built-in <property> to work like get/set functions.

In some situations, when it’s mandatory to hide the members, then you can use the property decorators to achieve getter/setter functionality.

That’s how you can modify your code.

3.2. Irregular use of Spaces with Tabs.

The PEP 8 guidelines affirm that Python code should consistently use four spaces for indentation and probit using tabs. Though, it’s just a piece of rules which no standard Python engine enforces. But that’s the way you should follow to make your code manageable and error free.

Anti-pattern example: Spaces mixed with tabs.

Here is a piece of Python code holding a class indented with tabs and two methods, one is using spaces and the other one has tabs for indentation. The code runs fine on execution but misses the PEP 8 guidelines.

Refactored: Convert Tabs to Spaces.

The solution is to refactor your code to convert the tabs into spaces. There are many ways to do it.

1. You can edit the settings of your text editor and set it to insert four spaces instead of a tab.

2. If you are on Linux and using VIM, then use the <:retab> command to do the job for you. It’ll swap tab with no. of spaces defined in the tab settings.

3. You can also run the script <> for auto-indentation. You can find it under the path <Python install dir>Tools\Scripts\>.

3.3. Underutilization of Python’s exception block.

While learning or adapting to a new language, we do consider walking through the essentials but walk over the extras.

However, we shouldn’t overlook a topic something like exceptions. Knowing and utilizing exceptions can make your application works even in exceptional conditions.

Sometimes, we get to use them but in a way that’s never going to help us. Let’s look at one such example followed by a solution that guides on implementing exceptions efficiently.

Anti-pattern: Not using exceptions at all.

Below is an example of weak error handling. It’s just confirming for an obvious fact. But overlooking the following conditions.

  • What if “debug.log” exist, but there comes some error while removing it. The code will abort without any informative message from the application.
  • You won’t want to see your code dying on a step that doesn’t impact the rest of the execution.

EAFP is a common slang often used by Python programmers. It stands for <easier to ask for forgiveness than permission>. It expresses a notion of using exceptions for handling errors relating to undefined variables or files etc.

Solution: Use try-except to avert any eventuality.

Here is the same code wrapped in a try-except block. It’s now in a format as per EAFP convention. Interestingly, the except clause is set to show the befitting error message.

3.4. Return inconsistent type values from functions.

You should check if your function is returning a value of a type that its caller doesn’t expect. If it does, then better update that condition to raise an exception. Otherwise, the caller would always have to verify the type before processing it further. 

You should avoid writing such code as it leads to confusion and increases complexity. Consider the below example and the refer to the solution given next.

Anti-pattern: Returning in-variant types.

In the below example, the function get_error_message() returns error messages corresponding to an error code. But in the case of a non-existent error code, it returns None. It leads to ambiguous code which is hard to maintain. And the caller will have to check it explicitly.

Solution: Raise an exception for unknown values.

The ideal approach for handling unexpected conditions is by employing try-except block and raise an appropriate exception. It also fits in such conditions because the function won’t return any data. So instead of returning any invalid or unknown value, better it throws an exception.

You can refer the code below which is the updated version of the above example. Now it’s much cleaner and doesn’t require checking for an additional data type.

3.5. Incorrect type checking.

Sometimes, the programmers use to call <type()> in their code to compare the datatypes. Instead, they should use <isinstance> for type checking.

This method does even have the ability to identify a derived class object. Hence, it is the best choice for type-checking.

Anti-pattern: Weak type-checking example.

The below code would fail to match the type of <emp> with Employee class. Though, the programmer would have thought that it would work.

Solution: Strong type-checking example.

Here is the right way to perform type checking of an object.


4. Imports leading to circular dependency.

In Python, import is also an executable statement. Each import clause leads to the execution of a corresponding module. Also, any function or a class embed in a module doesn’t come to life until the related code (in def or class) gets executed.

Hence, importing a module recursively may cause a circular dependency in your program. For example, let’s assume we’ve two modules mod1 and mod2.

The mod1 has import call to load mod2. It contains the following code.

To understand the reason of circular dependency, let’s imagine the following sequence of code.

1. You load the mod1 from your main program. The main program will then read the mod1 and process it. Since it’s loading the mod2 on the top, so Python will get on to reading it next.

2. Till this point, Python has got in both <mod1> and <mod2> under the sys.modules object list. But <mod1> still hasn’t received any definition because Python is currently executing the <mod2> module.

3. Now, to make a case of circular dependency, let’s add an “import mod1” statement into the mod2 module. So while executing the “import mod1” call, Python will reference back to the empty mod1 object.

4. In this situation, any call to mod1 entities (def or class) from mod2 would result in failures.


There can be two most likely solutions to the above problem.

1. Modify the program to eliminate the recursive imports. You can offload some functionality to a new module.

2. Another approach could be to displace the affected imports (mod2) to the tail of the calling module (i.e. mod1).

Hence, relocating the “import mod2” call towards the EOF in module mod1 will resolve the circular dependency issue.


5. Misusing the <__init__> method.

Just like constructors in C++, you’ve <__init__> method in Python. It automatically gets called when Python allocates memory to a new class object. The purpose of this method is to set the values of instance members for the class object.

And it’s not a good practice to explicitly return a value from the <__init__> method. It implies that you want to deviate from the purpose of this method. If that is the case, then better you choose a different method or define a new instance method for what you wish to accomplish.

Let’s establish the above fact with some examples.

Example: Misusing the <__init__> method.

In this example, the code is trying to return the work experience of an employee from the <__init__> method. But It’ll result in an error “TypeError: __init__() should return None”.

Example: Adding a new property to fix <__init__> error.

To solve the above issue, we’ll move the desired logic to a different instance method. You can call this method once the class object is ready with initialization.

So that were a few Python programming mistakes and their solutions we wanted to share with you. However, the list is too big to fit in one post. So we’ll keep posting the useful 🙂 programming mistakes in future as well.

Summary- Common Python Programming Mistakes to Avoid.

Hello, we believe this post had a lot for you to learn and apply in your daily work. You may not use them directly, but you can still avoid making such mistakes in your code.

Finally, if you’ve any such “Python programming mistakes” to share, then let the world know about it.

Also, you liked the stuff discussed here, then don’t mind it sharing it further.

Help us grow and grow with us.



Leave a Reply