Common mistakes we make in Python and why we should avoid that

Posted by Afsal on 25-Aug-2021

Hi Pythonistas!

As a developer, we all make mistakes while coding. Some of the mistakes are very silly and it takes a lot of effort while debugging. We are planning a series of blogs related to the common mistakes that I have made and explain why we should avoid them while coding. This is part 1 of this series.

Use of import *

Most of us use import * due to our laziness. But sometimes import * cause big issues and it takes a lot of time to debug this issue. I will explain this with an example.

I have 4 modules namely first.py, second.py, third.py, fourth.py

first.py

class MyClass(object):

	def __init__(self, name):
		self.name = name

	def __str__(self):
		return f"MyClass from first file: {self.name}"

second.py

class MyClass(object):

	def __init__(self, name, age):
		self.name = name
		self.age = age

	def __str__(self):
		return f"MyClass: {self.name} with age: {self.age}"

third.py

from first import MyClass

class MyClassExtended(MyClass):

	def __str__(self):
		return f"Extended class with name: {self.name}"

fourth.py

from second import MyClass

from third import *

print("\nI am creating my class extended\n")

my_ex = MyClassExtended("extendedclass")

print(my_ex)
print("\nI am Expecting MyClass is from second.py\n")

print("Actually my class is: ", MyClass, '\n')

my_class_2 = MyClass("Name", 2)

print(my_class_2)

When we see this code, MyClass is imported from the second module, and it has two parameters. We are initializing the class correctly. But if we run this code, output will be an exception like this

output

afsal@afsal-Mi-NoteBook-Horizon-Edition-14:~/Desktop/experiments/import_experiment$ python3 fourth.py 

I am creating my class extended

Extended classs with name: extendedclass

I am Expecting MyClass is from second.py

Actually my class is:  <class 'first.MyClass'> 

Traceback (most recent call last):
  File "fourth.py", line 16, in <module>
    my_class_2 = MyClass("Name", 2)
TypeError: __init__() takes 2 positional arguments but 3 were given
afsal@afsal-Mi-NoteBook-Horizon-Edition-14:~/Desktop/experiments/import_experiment$ 

Reason:

In fourth module we are importing * from third module and in third module we already imported MyClass from first module. Import * will copy the entire scope. As the statement “from second import MyClass is above the "from third import *" statement, the current scope of fourth module has  MyClass which is from first module. MyClass from second module is overwritten in this case. These kinds of errors are very hard to debug as the size of the repository is very big. 

corrected.py

from second import MyClass

from third import MyClassExtended

print("\nI am creating my class extended\n")

my_ex = MyClassExtended("extendedclass")

print(my_ex)

print("\nI am Expecting MyClass is from second.py\n")

print("Actually my class is: ", MyClass, '\n')

my_class_2 = MyClass("Name", 2)

print(my_class_2)

Output:

afsal@afsal-Mi-NoteBook-Horizon-Edition-14:~/Desktop/experiments/import_experiment$ python3 correct.py 

I am creating my class extended

Extended classs with name: extendedclass

I am Expecting MyClass is from second.py

Actually my class is:  <class 'second.MyClass'> 

MyClass: Name with age: 2
afsal@afsal-Mi-NoteBook-Horizon-Edition-14:~/Desktop/experiments/import_experiment$ 

Always import what you need.