Python中Test类被重复加载入内存的原因咨询
问题场景
有以下两个Python文件,执行module1.py后,"return new object"被打印了两次,且Test类出现了两个不同的id,与预期的仅加载一次不符。
module1.py 代码
class Test: data=None def get_data(): if Test.data is not None: print("return existing object") return Test.data else: Test.data="abc" print("return new object") return Test.data import module2 print("1:",Test.get_data(), "id:", id(Test)) print("2:",Test.get_data(), "id:", id(Test))
module2.py 代码
from module1 import Test print("3:",Test.get_data(), "id:", id(Test))
执行python module1.py后的输出
return new object 1: abc id: 2168979056288 return existing object 2: abc id: 2168979056288 return existing object 3: abc id: 2168979056288 return new object 1: abc id: 2168978913056 return existing object 2: abc id: 2168978913056
原因解释
这是Python模块加载机制的特性导致的,核心逻辑如下:
第一次加载:作为
__main__模块
当你直接运行module1.py时,Python会将其标记为__main__模块加载,此时会创建第一个Test类对象(对应输出中的第一个id)。第二次加载:作为普通模块被导入
当module1.py执行到import module2时,module2.py会被加载,其中的from module1 import Test语句会触发Python加载名为module1的普通模块。由于此时sys.modules中只有__main__,没有module1这个模块实例,Python会重新读取module1.py的代码,创建第二个完全独立的Test类对象(对应输出中的第二个id)。两次加载的模块是独立实体
Python中,__main__模块和同名的普通模块(如module1)是两个完全分开的模块实例,它们的全局命名空间、类对象、变量都是独立的。因此第二次加载时,新的Test类的data初始值为None,自然会再次触发"return new object"的打印。完整执行流程对应输出
- 第一步:
__main__模块(module1.py)定义第一个Test类,执行import module2; - 第二步:加载
module2.py,触发加载module1普通模块,该模块定义第二个Test类,执行自身的print语句(对应输出中后三行的return new object、1:、2:); - 第三步:
module2拿到第二个Test类,调用get_data()(此时data已被设置为abc,打印return existing object和3:); - 第四步:回到
__main__模块,执行自身的print语句,调用第一个Test类的get_data()(此时data还是None,打印return new object和1:、2:)。
- 第一步:
解决方案
方案1:隔离脚本执行代码
在module1.py中加入if __name__ == "__main__"判断,让顶层打印代码仅在直接运行时执行,避免模块被导入时重复执行:
class Test: data=None def get_data(): if Test.data is not None: print("return existing object") return Test.data else: Test.data="abc" print("return new object") return Test.data import module2 if __name__ == "__main__": print("1:",Test.get_data(), "id:", id(Test)) print("2:",Test.get_data(), "id:", id(Test))
方案2:使用独立入口脚本
创建单独的入口文件(如main.py),避免module1既作为__main__又作为普通模块被加载:
# main.py import module1
运行python main.py,此时module1只会被加载一次作为普通模块,Test类仅创建一次,"return new object"仅打印一次。
内容的提问来源于stack exchange,提问作者nayak0765

