0%

python循环调用解决

由于非科班出身,并没有学过什么软工之类的东西,写代码之前也不会画什么uml,所以在写python的时候很容易出现循环引用,耦合过重。下面给出我遇到这样的情况下的几个土办法蛤蛤😝。

情况分析

我遇到到的情况一般是,在文件A中引用B的一些函数等,在文件B中需要用到Ade方法或者类,要解决这种情况,斩断这种联系,就要减少一方对另一方的引用即依赖,减少耦合。

情况一

在一个文件中需要用到另一个文件的类,我的情况如下:

1
2
3
4
5
6
7
# A.py
class A

# B.py
import A.A as A
def func(a):
return A(some_act2(a))

即在B中需要对A的实例进行一些操作,并返回一个新的A的实例,主要是为了避免在B中的A的实例产生副作用。对于这种情况可以采用以下的解决办法避免引用

1
2
3
# B.py
def func(a):
return type(a)(some_act2(a))

这样就能避免导入A,还能对于多种类型的输入返回多种类型,轻松实现多态。

情况二

在一个文件中需要用到另一个文件的类,在这种情况下就没办法像上一种情况一样偷鸡了,按照谷歌的结果,出现循环引用的情况一般都是结构没组织好。最费时的办法就是重构这些代码,调整它们之间的结构,避免循环引用。

网上有人给出可以在尾部进行引用或者在函数内部引用的办法来解决循环引用,这涉及到了python引用库时进行的一些操作,或者采用土办法合并这两个文件即可。

1
2
3
4
5
6
7
8
9
10
# A.py
def b():
c()

from B import c

# B.py
def b():
from B import c
c()

还可以采用直接导入模块名的办法,通过模块名来调用其中的函数

1
2
3
4
5
6
7
8
9
10
11
12
# A.py
import b
def a():
b.c()

# B.py
import a
def b():
a.a()

def c():
pass

原理分析

import原理可参见这篇博文,但这篇博文没有给出循环调用的报错的原因。更详细的介绍可看这篇博文

下面都以python a.py作为入口

错误示例分析

两个错误文件示范

1
2
3
4
5
6
7
8
9
# a.py
from b import b
def a():
pass

# b.py
from a import a
def b():
pass

下面给出具体的原因分析,主要注意sys.modules和其中的变量,循环引用报错的原因是ImportError: cannot import name 'b' from 'b'

a.py第一句中from b import b这时开始导入b.pysys.modules中添加了b模块,进入执行b.py,然后from a import a,导入asys.modules中添加a,注意这时后面的def b还没执行,所以sys.modules['b']中还没有b变量,所以在执行a模块进行from b import b时就会报错找不到b

一个正确的导入分析

文件示例

1
2
3
4
5
6
7
8
9
# a.py
import b
def a():
b.b()

# b.py
import a
def b():
pass

aimport b,在sys.modules添加b,执行bimport a,在sys.modules中添加a,在a中执行import b,发现sys.modules中已经有b,直接进入后面的语句,执行完成之后,把变量载入sys.modules['a']中,返回b继续执行def b,执行完成后加入sys.modules['b'],最后回到a

分析

所以产生错误的原因,主要在于导入机制中sys.modules中模块的初始化和其中变量的添加的时间点的先后导致的,sys.modules应该是为了避免循环引用导致无限循环所采用的机制,如果没有这个,就会一直执行import。所以应该在import的时候就添加到sys.modules中,所以此处能否先执行后面的先添加变量到对应的模块中,再执行导入引用模块,解决这个问题。可能python作者这样做有他们更深层次的原因吧,我作为局外人还在第一层,不懂他们999层人的想法。