魔幻离现实仅一步之遥:细说Python的循环调用、循环引用和循环导入

小时候,常被一些可笑的问题困扰——尽管成年以后面临的疑惑更多,但似乎是因为已经适应了在迷茫中前行,对于未解的问题反倒是失去了那种急于想知道答案的迫切感。比如,站在两面相对的镜子中间,会看到无数个自己吗?对于少时的我,这的确是一个非常魔幻的问题,直到理解了光量子能量衰减,才算找到了答案。
在这里插入图片描述
近日,有同学咨询Python对象的循环引用以及垃圾回收问题,结合前些日子遇到的循环调用和循环导入问题,在整理答案的时候,我忽然意识到,这几个问题居然和困惑我多年的“两面镜子”问题居然有相通之处:看起来都有些魔幻,转身即是真实的世界!

1. 走向毁灭的函数循环调用

如果多个函数相互调用,构成闭环,就形成了函数的循环调用。下面的例子中,函数a在其函数体中调用了函数b,而函数b在其函数体中又调用了函数a,这就是典型的函数循环调用。

>>> def a():
		print('我是a')
		b()
	
>>> def b():
		print('我是b')
		a()
	
>>> a()

此种情况下,调用函数(无论是a函数还是b函数),会发生什么呢?

>>> a()
我是a
我是b
我是a
我是b
...... # 此处省略了一千余行
Traceback (most recent call last):
  File "<pyshell#64>", line 1, in <module>
    a()
  File "<pyshell#59>", line 3, in a
    b()
...... # 此处省略了两千余行
RecursionError: maximum recursion depth exceeded while pickling an object

很快你就会发现,运行出现了问题,系统连续抛出异常,大约滚动了几千行之后,终于结束了运行。最后的提示是:

RecursionError: maximum recursion depth exceeded while pickling an object

意思是说,发生了递归错误,在序列化(pickle)对象时超过了最大递归深度。

原来,循环调用类似于递归调用,为了保护堆栈不会溢出,Python环境一般都会设置递归深度保护,一旦查过递归深度,就会抛出递归错误,然后再一层一层退出堆栈。这就是屏幕滚动几千条错误信息的原因。

关于Python环境递归深度,可以通过sys模块查看和设置。

>>> import sys
>>> sys.getrecursionlimit()
1000
>>> sys.setrecursionlimit(500)
>>> sys.getrecursionlimit()
500

2. 同生共死的对象循环引用

函数的循环调用不难理解,而对象的循环引用就有点费解了。什么是对象的循环引用呢?当一个对象被创建时(比如实例化一个类),Python会为这个对象设置一个引用计数器。如果这个对象被引用,比如被关联到一个变量名,则该对象的引用计数器加1,如果关联关系取消,则该对象的引用计数器减1。当一个对象的引用计数器为1时(关于这一点,仅凭个人观察得出,未见权威说法),系统将自动回收该对象。这就是Python的垃圾回收机制。下面的代码,借助于sys模块,可以直观地看到一个列表对象的引用计数器的变化。

>>> import sys
>>> a = list('abc')
>>> sys.getrefcount(a)
2
>>> b = a
>>> sys.getrefcount(a)
3
>>> del b
>>> sys.getrefcount(a)
2

当多个对象存在相互间的成员引用,一旦形成闭环的时候,就会发生所谓对象的循环引用。我们来看一个例子:a和b是类A的两个实例对象,del这两个对象的时候,将会调用对象的__del__方法,最后显示“运行结束”。

class A:
    def __init__(self, name, somebody=None):
        self.name = name
        self.somebody = somebody
        print('%s: init'%self.name)
    def __del__(self):
        print('%s: del'%self.name)

a = A('a')
b = A('b')

del a
del b

print('运行结束')

运行结果正如我们所希望的那样。

a: init
b: init
a: del
b: del
运行结束

然而,当我们创建了实例a和b之后,如果将a.somebody指向b,将b.somebody指向a,那么就产生了实例间成员相互引用形成闭环的情况。

class A:
    def __init__(self, name, somebody=None):
        self.name = name
        self.somebody = somebody
        print('%s: init'%self.name)
    def __del__(self):
        print('%s: del'%self.name)

a = A('a')
b = A('b')

a.somebody = b
b.sombody = a

del a
del b

print('运行结束')

运行这段代码,你会发现,del这两个对象的时候,对象的__del__方法并没有被立即执行,而是程序结束之后才被执行的。

a: init
b: init
运行结束
a: del
b: del

这意味着,在程序运行期间,应该被回收的内存并没有正确回收。这样的问题,属于内存泄漏,应该给予高度重视。通常,我们可以使用gc模块强制回收内存。

import gc

class A:
    def __init__(self, name, somebody=None):
        self.name = name
        self.somebody = somebody
        print('%s: init'%self.name)
    def __del__(self):
        print('%s: del'%self.name)

a = A('a')
b = A('b')

a.somebody = b
b.sombody = a

del a
del b

gc.collect()

print('运行结束')

再看运行结果,一切正常了。

a: init
b: init
a: del
b: del
运行结束

3. 转圈推磨的模块循环导入

相对而言,模块的循环导入的情况一般极少发生。如果发生,一定是模块的功能分割不合理造成的,通过调整模块的定义,可以很容地解决问题。下面用一个最精简的例子,来演示一下模块循环导入是如何产生的。

名为a.py的脚本文件内容如下:

import b

MODULE_NAME = 'a'
print(b.MODULE_NAME)

名为b.py的脚本文件内容如下:

import a

MODULE_NAME = 'b'
print(a.MODULE_NAME)

两个脚本互相引用,并且各自使用了对方定义的常量MODULE_NAME。无论我们运行哪个脚本,都会因为模块的循环导入而无法正确执行。

Traceback (most recent call last):
File “a.py”, line 1, in
import b
File “D:\temp\csdn\b.py”, line 1, in
import a
File “D:\temp\csdn\a.py”, line 4, in
print(b.MODULE_NAME)
AttributeError: module ‘b’ has no attribute ‘MODULE_NAME’

相关推荐
<p> <strong><span style="font-size:20px;color:#FF0000;">本课程主要针对计算机相关专业正在做毕设学生与需要项目实战练习Java学习者</span></strong> </p> <p> <span style="color:#FF0000;"><strong><span style="font-size:18px;">1. 包含<span style="color:#FFFF00;background-color:#FF0000;">项目源码</span><span style="color:#FFFF00;background-color:#FF0000;">项目文档数据库脚本软件工具</span>等所有资料</span></strong></span> </p> <p> <span style="color:#FF0000;"><strong><span style="font-size:18px;">2. 手把手带你从零开始部署运行本套系统</span></strong></span> </p> <p> <span style="color:#FF0000;"><strong><span style="font-size:18px;">3. 该项目附带源码资料可作为毕设使用</span></strong></span> </p> <p> <span style="color:#FF0000;"><strong><span style="font-size:18px;">4. 提供技术答疑远程协助指导</span></strong></span><strong><span style="font-size:18px;"></span></strong> </p> <p> <br /> </p> <p> <span style="font-size:18px;"><strong>项目运行截图</strong></span> </p> <p> <strong><span style="font-size:18px;">1)系统登陆界面</span></strong> </p> <p> <strong><span style="font-size:18px;"><img src="https://img-bss.csdn.net/202002241015433522.png" alt="" /><br /> </span></strong> </p> <p> <strong><span style="font-size:18px;"><strong><span style="font-size:18px;">2)学生模块</span></strong></span></strong> </p> <p> <strong><span style="font-size:18px;"><img src="https://img-bss.csdn.net/202002241015575966.png" alt="" /></span></strong> </p> <p> <strong><span style="font-size:18px;"><strong><span style="font-size:18px;">3)教师模块</span></strong></span></strong> </p> <p> <strong><span style="font-size:18px;"><img src="https://img-bss.csdn.net/202002241016127898.png" alt="" /></span></strong> </p> <p> <strong><span style="font-size:18px;"><strong><span style="font-size:18px;">4)系统管理员</span></strong></span></strong> </p> <p> <strong><span style="font-size:18px;"><img src="https://img-bss.csdn.net/202002241016281177.png" alt="" /></span></strong> </p> <p> <strong><span style="font-size:18px;"><img src="https://img-bss.csdn.net/202002241016369884.png" alt="" /></span></strong> </p> <p> <strong><span style="font-size:18px;"><br /> </span></strong> </p> <p> <strong><span style="font-size:18px;"><strong><span style="font-size:18px;">更多Java毕设项目请关注我毕设系列课程 <a href="https://edu.csdn.net/lecturer/2104">https://edu.csdn.net/lecturer/2104</a></span></strong></span></strong> </p> <p> <strong><span style="font-size:18px;"><br /> </span></strong> </p>
<p> 课程演示环境Windows10  </p> <p> 需要学习<span>Ubuntus</span>系统<span>YOLOv4-tiny</span>同学请前往《<span>YOLOv4-tiny</span>目标检测实战训练自己数据集》 <span></span> </p> <p> <span> </span> </p> <p> <span style="color:#E53333;">YOLOv4-tiny</span><span style="color:#E53333;">来了!速度大幅提升!</span><span></span> </p> <p> <span> </span> </p> <p> <span>YOLOv4-tiny</span>在<span>COCO</span>上性能可达到<span>40.2% AP50, 371 FPS (GTX 1080 Ti)</span>。相较于<span>YOLOv3-tiny</span>,<span>AP</span><span>FPS</span>性能有巨大提升。并且,<span>YOLOv4-tiny</span>权重文件只有<span>23MB</span>,适合在移动端嵌入式设备边缘计算设备上部署。<span></span> </p> <p> <span> </span> </p> <p> 本课程将手把手地教大家使用<span>labelImg</span>标注使用<span>YOLOv4-tiny</span>训练自己数据集。课程实战分为两个项目单目标检测(足球目标检测)多目标检测(足球梅西同时检测)。<span></span> </p> <p> <span> </span> </p> <p> 本课程<span>YOLOv4-tiny</span>使用<span>AlexAB/darknet</span>,在<span>Windows10</span>系统上做项目演示。包括<span>YOLOv4-tiny</span>网络结构安装<span>YOLOv4-tiny</span>标注自己数据集整理自己数据集修改配置文件训练自己数据集测试训练出网络模型性能统计<span>(mAP</span>计算<span>)</span>先验框聚类分析。 <span> </span> </p> <p> <span> </span> </p> <p> 除本课程《<span>Windows</span>版<span>YOLOv4-tiny</span>目标检测实战训练自己数据集》外,本人推出了有关<span>YOLOv4</span>目标检测系列课程。请持续关注该系列其它视频课程,包括<span></span> </p> <p> 《<span>Windows</span>版<span>YOLOv4</span>目标检测实战训练自己数据集》<span></span> </p> <p> 《<span>Windows</span>版<span>YOLOv4</span>目标检测实战人脸口罩佩戴识别》<span></span> </p> <p> 《<span>Windows</span>版<span>YOLOv4</span>目标检测实战中国交通标志识别》<span></span> </p> <p> 《<span>Windows</span>版<span>YOLOv4</span>目标检测原理与源码解析》<span></span> </p> <p> <span> <img alt="" src="https://img-bss.csdnimg.cn/202007061503586145.jpg" /></span> </p> <p> <span><img alt="" src="https://img-bss.csdnimg.cn/202007061504169339.jpg" /><br /> </span> </p>
©️2020 CSDN 皮肤主题: 代码科技 设计师:Amelia_0503 返回首页
实付 15.20元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值