2022年 11月 5日

python循环引用的处理

python循环引用的处理

假设同一文件夹下存在三个文件:a.py, b.py 和run.py

a和b中定义A,B,C类,run.py执行调用

a中定义类A和C,b中定义类B,其中B是A的子类,C是B的子类,

因此b.py中需要import A,a.py中需要import B,造成循环引用

文件内容分别如下(报错版):

a.py

from b import B


class A:
    name = 'A'

    def print_self(self):
        print(self.name)


class C(B):
    name = 'C'


c = C()
c.print_self()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

b.py

from a import A


class B(A):
    name = 'B'
  • 1
  • 2
  • 3
  • 4
  • 5

run.py

from a import A, B, C

test_A = A()
test_B = B()
test_C = C()
test_A.print_self()
test_B.print_self()
test_C.print_self()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

运行run.py,运行结果:
在这里插入图片描述
结果说明:
run.py中执行from a import A, B, C时,由于a.py中的B是从b.py中import过来的,
会到b.py中执行代码,遇到第一行from a import A,又回到a.py中执行from b import B,造成无限循环,编译器报错。

那怎么处理呢?
注意到B继承自A,C继承自B,我们可以在需要使用到B时再import B,也即把a.py修改如下(正确运行版):

class A:
    name = 'A'

    def print_self(self):
        print(self.name)


from b import B


class C(B):
    name = 'C'


c = C()
c.print_self()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

运行结果:
在这里插入图片描述
1.第一个”C”由A.py中c.print_self()输出
2.”A”由run.py中test_A.print_self()输出
3.”B”由run.py中test_B.print_self()输出
4.第二个“C”由run.py中test_C.print_self()输出

执行顺序:
1.from a import A, B, C时,会把a.py中的代码执行一遍
2.首先声明class A
3.执行到第8行from b import B,到b.py中,执行from a import A,由于A已经被声明,在这里返回一个A的引用,给B作父类(这是由于python编译器把每个模块都视为单例,已经import的模块便不再import,只是在b.py中建立一个A类的引用,供B类的声明与初始化)。
4.回到a.py中声明C,初始化c,执行c.print_self(),输出第一个”C”
5.a.py走完一遍,A,B,C均已声明,from a import A, B, C返回A,B,C三个类的引用
6.run.py继续往下执行,完成三个类的初始化与方法调用,依次输出“A”,“B”,“C”

以上为能解释现象的执行顺序,仅供参考,是否正确,需要到python源码中找解答

———————————update———————————
上述方案能执行,但理解上是不正确的,因为直接执行a.py也报错:
在这里插入图片描述