Python中的循环(或循环)导入

本文翻译自:Circular (or cyclic) imports in Python

What will happen if two modules import each other? 如果两个模块相互导入会怎样?

To generalize the problem, what about the cyclic imports in Python? 为了概括这个问题,Python中的循环导入怎么办?


#1楼

参考:https://stackoom.com/question/37e1/Python中的循环-或循环-导入


#2楼

Ok, I think I have a pretty cool solution. 好的,我想我有一个很酷的解决方案。 Let's say you have file a and file b . 假设您有文件a和文件b You have a def or a class in file b that you want to use in module a , but you have something else, either a def , class , or variable from file a that you need in your definition or class in file b . 你有一个defclass文件b要在模块使用a ,但你有别的东西,无论是defclass ,或从文件变量a是你在你的文件中定义或类需要b What you can do is, at the bottom of file a , after calling the function or class in file a that is needed in file b , but before calling the function or class from file b that you need for file a , say import b Then, and here is the key part , in all of the definitions or classes in file b that need the def or class from file a (let's call it CLASS ), you say from a import CLASS 你可以做的是,在文件的底部a ,调用文件中的函数或下课后a是需要在文件b ,但是从文件调用的函数或类之前, b ,你需要的文件a ,说import b然后,这是关键部分 ,在文件b中所有需要从文件a获取defclass的定义或类中(我们将其称为CLASS ),您from a import CLASS

This works because you can import file b without Python executing any of the import statements in file b , and thus you elude any circular imports. 这工作,因为你可以导入文件b没有的Python执行任何文件导入语句的b ,这样的话你逃避任何圆形进口。

For example: 例如:

File a: 档案a:

class A(object):

     def __init__(self, name):

         self.name = name

CLASS = A("me")

import b

go = B(6)

go.dostuff

File b: 文件b:

class B(object):

     def __init__(self, number):

         self.number = number

     def dostuff(self):

         from a import CLASS

         print "Hello " + CLASS.name + ", " + str(number) + " is an interesting number."

Voila.


#3楼

As other answers describe this pattern is acceptable in python: 正如其他答案所描述的那样,这种模式在python中是可以接受的:

def dostuff(self):
     from foo import bar
     ...

Which will avoid the execution of the import statement when the file is imported by other modules. 当其他模块导入文件时,这将避免执行import语句。 Only if there is a logical circular dependency, this will fail. 仅当存在逻辑循环依赖关系时,这才会失败。

Most Circular Imports are not actually logical circular imports but rather raise ImportError errors, because of the way import() evaluates top level statements of the entire file when called. 大多数循环导入实际上不是逻辑循环导入,而是引发ImportError错误,这是因为import()在调用时会评估整个文件的顶级语句的方式。

These ImportErrors can almost always be avoided if you positively want your imports on top : 如果您确实希望将导入ImportErrors ,那么几乎可以避免这些ImportErrors

Consider this circular import: 考虑以下循环导入:

App A 应用程式A

# profiles/serializers.py

from images.serializers import SimplifiedImageSerializer

class SimplifiedProfileSerializer(serializers.Serializer):
    name = serializers.CharField()

class ProfileSerializer(SimplifiedProfileSerializer):
    recent_images = SimplifiedImageSerializer(many=True)

App B 应用程式B

# images/serializers.py

from profiles.serializers import SimplifiedProfileSerializer

class SimplifiedImageSerializer(serializers.Serializer):
    title = serializers.CharField()

class ImageSerializer(SimplifiedImageSerializer):
    profile = SimplifiedProfileSerializer()

From David Beazleys excellent talk Modules and Packages: Live and Let Die! 大卫·比兹利(David Beazleys)精彩演讲模块和软件包:活着,让自己死! - PyCon 2015 , 1:54:00 , here is a way to deal with circular imports in python: - 2015年PYCON1:54:00 ,在这里是为了对付在python圆形进口方式:

try:
    from images.serializers import SimplifiedImageSerializer
except ImportError:
    import sys
    SimplifiedImageSerializer = sys.modules[__package__ + '.SimplifiedImageSerializer']

This tries to import SimplifiedImageSerializer and if ImportError is raised, because it already is imported, it will pull it from the importcache. 这会尝试导入SimplifiedImageSerializer ,如果引发了ImportError ,因为已经导入了它,它将从importcache中将其拉出。

PS: You have to read this entire post in David Beazley's voice. PS:您必须以David Beazley的声音阅读整篇文章。


#4楼

I completely agree with pythoneer's answer here. 我完全同意pythoneer的回答。 But I have stumbled on some code that was flawed with circular imports and caused issues when trying to add unit tests. 但是我偶然发现了一些循环导入有缺陷的代码,并在尝试添加单元测试时引起了问题。 So to quickly patch it without changing everything you can resolve the issue by doing a dynamic import. 因此,在不做任何更改的情况下快速修补它,可以通过动态导入解决问题。

# Hack to import something without circular import issue
def load_module(name):
    """Load module using imp.find_module"""
    names = name.split(".")
    path = None
    for name in names:
        f, path, info = imp.find_module(name, path)
        path = [path]
    return imp.load_module(name, f, path[0], info)
constants = load_module("app.constants")

Again, this isn't a permanent fix but may help someone that wants to fix an import error without changing too much of the code. 同样,这不是永久性的修复,但是可以帮助想要修复导入错误而无需更改太多代码的人。

Cheers! 干杯!


#5楼

Circular imports can be confusing because import does two things: 循环导入可能会造成混淆,因为导入有两件事:

  1. it executes imported module code 它执行导入的模块代码
  2. adds imported module to importing module global symbol table 将导入模块添加到导入模块全局符号表中

The former is done only once, while the latter at each import statement. 前者仅执行一次,而后者在每个import语句中执行。 Circular import creates situation when importing module uses imported one with partially executed code. 当导入模块使用部分执行代码的已导入模块时,循环导入会产生情况。 In consequence it will not see objects created after import statement. 因此,它将不会看到import语句之后创建的对象。 Below code sample demonstrates it. 下面的代码示例对此进行了演示。

Circular imports are not the ultimate evil to be avoided at all cost. 循环进口并不是不惜一切代价避免的最终罪恶。 In some frameworks like Flask they are quite natural and tweaking your code to eliminate them does not make the code better. 在诸如Flask之类的某些框架中,它们是很自然的,调整您的代码以消除它们并不能使代码变得更好。

main.py main.py

print 'import b'
import b
print 'a in globals() {}'.format('a' in globals())
print 'import a'
import a
print 'a in globals() {}'.format('a' in globals())
if __name__ == '__main__':
    print 'imports done'
    print 'b has y {}, a is b.a {}'.format(hasattr(b, 'y'), a is b.a)

b.by b.by

print "b in, __name__ = {}".format(__name__)
x = 3
print 'b imports a'
import a
y = 5
print "b out"

a.py py

print 'a in, __name__ = {}'.format(__name__)
print 'a imports b'
import b
print 'b has x {}'.format(hasattr(b, 'x'))
print 'b has y {}'.format(hasattr(b, 'y'))
print "a out"

python main.py output with comments 带有注释的python main.py输出

import b
b in, __name__ = b    # b code execution started
b imports a
a in, __name__ = a    # a code execution started
a imports b           # b code execution is already in progress
b has x True
b has y False         # b defines y after a import,
a out
b out
a in globals() False  # import only adds a to main global symbol table 
import a
a in globals() True
imports done
b has y True, a is b.a True # all b objects are available

#6楼

Module a.py : 模块a.py:

import b
print("This is from module a")

Module b.py 模块b.py

import a
print("This is from module b")

Running "Module a" will output: 运行“模块a”将输出:

>>> 
'This is from module a'
'This is from module b'
'This is from module a'
>>> 

It output this 3 lines while it was supposed to output infinitival because of circular importing. 它输出这3行,而由于循环导入而应该输出无穷大。 What happens line by line while running"Module a" is listed here: 下面列出了在运行“模块a”时逐行发生的情况:

  1. The first line is import b . 第一行是import b so it will visit module b 因此它将访问模块b
  2. The first line at module b is import a . 模块b的第一行是import a so it will visit module a 因此它将访问模块a
  3. The first line at module a is import b but note that this line won't be executed again anymore , because every file in python execute an import line just for once, it does not matter where or when it is executed. 模块a的第一行是import b请注意,此行将不再执行 ,因为python中的每个文件仅执行一次import行,因此无论在何时何地执行它都无关紧要。 so it will pass to the next line and print "This is from module a" . 因此它将传递到下一行并显示"This is from module a"
  4. After finish visiting whole module a from module b, we are still at module b. 从模块b访问完整个模块a后,我们仍在模块b中。 so the next line will print "This is from module b" 所以下一行将打印"This is from module b"
  5. Module b lines are executed completely. 模块b行完全执行。 so we will go back to module a where we started module b. 因此,我们将回到模块b的起始模块a。
  6. import b line have been executed already and won't be executed again. import b行已经执行,将不再执行。 the next line will print "This is from module a" and program will be finished. 下一行将打印"This is from module a"并且程序将完成。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值