开发者

Why is Python more strict with circular imports when using from-imports?

开发者 https://www.devze.com 2023-04-08 07:36 出处:网络
I know that Python discourages any situation which can get you into a circular import. But I wanted understand the Python internals of why from-imports are seemingly arbitrarily less forgiving than no

I know that Python discourages any situation which can get you into a circular import. But I wanted understand the Python internals of why from-imports are seemingly arbitrarily less forgiving than normal imports in circular import situations.

For example, this code compiles:

# main.py
import CommonUtil

# commonutil.py
import util
class CommonUtil:
    # some code that uses util.Util
    pass

# util.py
import commonutil
class Util:
    # some code that uses commonutil.CommonUtil
    pass

But this 开发者_开发技巧code does not:

# main.py
import CommonUtil

# commonutil.py
import util
class CommonUtil:
    # some code that uses util.Util
    pass

# util.py
from commonutil import CommonUtil
class Util:
    # some code that uses CommonUtil
    pass

Traceback (most recent call last):
  File "main.py", line 1, in <module>
    import CommonUtil
  File "commonutil.py", line 1, in <module>
    import util
  File "util.py", line 1, in <module>
    from commonutil import CommonUtil
ImportError: cannot import name CommonUtil

You don't hit compiler errors as long as you don't try to use the relevant classes before all the imports have completed. But when you try to do some aliasing, then it fails. Can someone explain what's going on internally in the Python that causes this error to rear its head only when from-import is used? And secondarily, is there any easy way around this? (Besides the obvious "pull shared code out to a third module," which I'll likely do anyways.)


Modules are executed from top to bottom. When an import is seen for the first time, execution of the current module is suspended so that the other module can be imported. When the other module attempts to import the first module, it gets a reference to the currently partially-executed module. Since code located after the import of the other module hasn't yet been executed, any names contained within it cannot yet exist.

main.py

import a

a.py

var1 = 'foo'
import b
var2 = 'bar'

b.py

import a
print a.var1 # works
print a.var2 # fails

The way around it is to not access the names in the imported module until its execution has completed.


see http://effbot.org/zone/import-confusion.htm#circular-imports for an explanation of what is happening.

I assume you launch the main.py file. Python will first try to load commonutil. It will create a module object, and start filling it with class and function and global variable when encountering their definition. The first statement is an import, so now python creates the util module and start filling it. The common module exist, but is empty. In the first version, you do not access any commonutil object at load time, so everything is fine. In the second one, you try to fetch a specific variable in commonutil which does not exist at this moment. If you had used something like f(commonutil.CommonUtil) in the first version, it would have also crashed.

0

精彩评论

暂无评论...
验证码 换一张
取 消