Dmitry K.
Dmitry K.

Reputation: 1195

Calling isinstance in main Python module

There is a strange behavior of isinstance if used in __main__ space.

Consider the following code

a.py:

class A(object):
    pass

if __name__ == "__main__":
    from b import B
    b = B()
    print(isinstance(b, A))

b.py

from a import A
class B(A):
    pass

main.py

from a import A
from b import B

b = B()
print(isinstance(b, A))

When I run main.py, I get True, as expected, but when I run a.py, I am getting False. It looks like the name of A is getting the prefix __main__ there.

How can I get a consistent behavior? I need this trick with import of B in a.py to run doctest on file a.py.

Upvotes: 6

Views: 513

Answers (2)

unutbu
unutbu

Reputation: 879591

Chain of events:

  1. The a.py script is called.
  2. The class A statement is executed, creating A in the __main__ namespace.
  3. b is imported.
  4. In b.py a is imported.
  5. The class A statement is executed, creating A in the a namespace. This A in the a namespace has no relation to the A in the __main__ namespace other than having been generated by the same code. They are different objects.

I agree with Helmut that it is best to avoid such circular imports. However, if you wish to fix your code with minimal changes, you could do the following:

Let's rename b.py --> bmodule.py so we can distinguish b the module from b the variable (hopefully in your real code these names are already distinct):

class A(object):
    pass

if __name__ == "__main__":
    import bmodule
    b = bmodule.B()
    print(isinstance(b, bmodule.A))

prints

True

Upvotes: 2

Helmut Grohne
Helmut Grohne

Reputation: 6778

WSo what happens when you run a.py is that Python reads a.py and executes it. While doing so, it imports module b, which imports module a, but it does not reuse the definitions from parsing it earlier. So now you have two copies of the definitions inside a.py, known as modules __main__ and a and thus different __main__.A and a.A.

In general you should avoid importing modules that you are executing as well. Rather you can create a new file for running doctests and use something like

import a
import doctest

doctest.testmod(a)

and remove the __main__ part from module a.

Upvotes: 7

Related Questions