Need for __name__ == "__main__" as seen in circular import problems

There is a circular import problem. For example

hoge.py


# coding: utf-8

import piyo

class Base:
	pass

class Hoge(Base):
	pass

When,

piyo.py


# coding: utf-8

import hoge

class Piyo(hoge.Base):
	pass

If there are two Python scripts, " python3 hoge.py "will not cause an error, but if you execute" python3 piyo.py",

Traceback (most recent call last):
  File "piyo.py", line 3, in <module>
    import hoge
  File "hoge.py", line 3, in <module>
    import piyo
  File "piyo.py", line 5, in <module>
    class Piyo(hoge.Base):
AttributeError: partially initialized module 'hoge' has no attribute 'Base' (most likely due to a circular import)

AttributeError will occur like this. (Looking now, he politely says "most likely due to a circular import") One solution, on the other hand, is to make the Base class a separate script and import that script from both hoge.py / piyo.py.

By the way, when such a problem occurs occasionally, it comes up on the Q & A site, but the answer can be the same as above, but in fact the cause of the exception is quite deep, and always "Hmm. I was worried, and when I understood the reason, I thought, "Oh, this is the one I was worried about before. See you again." Today, I'll make a note of that.


Now, here are some Python specs you need to know to understand the cause of this problem.

Now, with this in mind, let's verify what happens when we run the previous " python3 piyo.py ".

  1. In " piyo.py "," ʻimport hoge "and hoge.pyare imported. At this point, thePiyo` class has not yet been defined.
  2. In the imported " hoge.py "," ʻimport piyo" is executed, but since " piyo.py` "is already loaded, it does nothing.
  3. The Base class and Hoge class written after " hoge.py "are defined.

As you can see, it runs without any problems ... that? Something is different.

Now let's run " python3 hoge.py ".

  1. In " hoge.py "," ʻimport piyo "and piyo.pyare imported. At this point, theBase and Hoge` classes are undefined.
  2. In the imported " piyo.py "," ʻimport hoge" is executed, but since " hoge.py` "is already loaded, it does nothing.
  3. The hoge.Base class is referenced as the derived class of the Piyo class, but the Base class definition is not executed in hoge.py, so the hoge module There is no Base in the attribute and I get a ʻAttributeError` exception.

In this way, an error occurs ... that?

In this way, if you find the cause, it will be the opposite of the actual operation. I always have a problem here, but I remember another Python specification and solve it.

Based on this, let's verify the operation of " python3 piyo.py "again.

  1. In " piyo.py "," ʻimport hoge "and hoge.pyare imported. At this point, thePiyo` class has not yet been defined.
  2. In the imported " hoge.py "," ʻimport piyo" is executed. " Piyo.py"is already loaded, but it is ** the module name is"main" **, and the module " piyo"is not loaded, so"piyo again .py "is executed. At this point, the Base and Hoge classes that follow are not running, so they are not yet defined.
  3. In the imported " piyo.py "," ʻimport hoge" is executed, but since " hoge.py` "is already loaded, it does nothing.
  4. The hoge.Base class is referenced as the derived class of the Piyo class, but since the Base class definition is not executed in hoge.py, the hoge module There is no Base in the attribute and I get a ʻAttributeError` exception.

Sure, you get a ʻAttributeError` exception. As a test,

piyo.py


# coding: utf-8

import hoge
import sys
print('\n'.join([repr(n) for n in sys.modules.items()]))

class Piyo(hoge.Base):
	pass

Let's take a look at the contents of sys.modules.

('__main__', <module '__main__' from 'a/piyo.py'>)
('piyo', <module 'piyo' from '/path/to/a/piyo.py'>)

(Omitted except necessary) As mentioned above, you can see that " piyo.py "is loaded twice.


Now, let's review.

Basically, once a script is imported, it will not be executed twice, but a script that is executed at startup may be executed twice. So the script that is executed at startup is

if __name__ == '__main__':
    main()

So, you should look at your module name and run it only the first time.

Recommended Posts

Need for __name__ == "__main__" as seen in circular import problems
Need for __name__ == "__main__" as seen in circular import problems
Search for strings in Python
Search for strings in files
Techniques for sorting in Python
About "for _ in range ():" in python
About the need for the first slash in the subscriber name and publisher name
About the need for the first slash in the subscriber name and publisher name