模块级别的实例
# %load x.py
class MyClass:
def __init__(self):
print("Initializing MyClass instance")
self.value = 42
my_instance = MyClass()
输出:Initializing MyClass instance
# file1.py
import x
import sys
print('===file1===')
print(x.my_instance.value)
# file2.py
import x
import sys
print('===file2===')
print(x.my_instance.value)
输出:
Initializing MyClass instance
=file1=
42
=file2=
42
从这个案例可以看出,file2加载x.py的时候,没有初始化,直接引用已有的实例
函数级别的实例
# %load x.py
class MyClass:
def __init__(self):
print("Initializing MyClass instance")
self.value = 42
def get_instance():
return MyClass()
# file1.py
import x
instance1 = x.get_instance()
print(instance1.value)
# file2.py
import x
instance2 = x.get_instance()
print(instance2.value)
Initializing MyClass instance
42
Initializing MyClass instance
42
结论
- 模块级别的实例:如果你在模块中定义了一个实例,并且在多个文件中导入这个模块,那么这个实例只会被初始化一次。
- 函数级别的实例:如果你在函数中创建实例,并在多个文件中调用这个函数,那么每次调用函数时,实例都会被重新初始化。
实现原理是什么呢
1. 模块缓存(Module Cache)
Python使用一个全局字典sys.modules来缓存已经导入的模块。这个字典的键是模块名,值是模块对象。
import sys
print(sys.modules)
当你第一次导入一个模块时,Python会检查sys.modules字典,如果模块已经存在,则直接返回缓存的模块对象;如果不存在,则执行模块的代码并将其添加到sys.modules中。
2. 模块查找路径(Module Search Path)
Python使用sys.path列表来确定模块的查找路径。当你导入一个模块时,Python会按照sys.path中的路径顺序查找模块文件。
import sys
print(sys.path)
3.模块加载(Module Loading)
当Python找到模块文件后,它会执行以下步骤:
- 创建模块对象:Python会创建一个新的模块对象,并将其添加到sys.modules字典中。
- 执行模块代码:Python会执行模块文件中的代码,初始化模块中的所有内容(包括变量、函数、类和实例)。
- 返回模块对象:执行完模块代码后,Python会返回模块对象,供导入语句使用。
4.后续导入(Subsequent Imports)
当同一个模块再次被导入时,Python会直接从sys.modules字典中获取已经加载的模块对象,而不会重新执行模块的代码。
具体过程
首次导入:
当你第一次导入x.py时(例如在file1.py中),Python会执行以下步骤:
检查sys.modules字典,发现x模块不存在。
创建一个新的模块对象,并将其添加到sys.modules字典中。
执行x.py中的代码,初始化MyClass的实例,并将其赋值给my_instance变量。
返回模块对象。
输出:Initializing MyClass instance。
具体过程
首次导入:
-
当你第一次导入x.py时(例如在file1.py中),Python会执行以下步骤:
检查sys.modules字典,发现x模块不存在。 创建一个新的模块对象,并将其添加到sys.modules字典中。 执行x.py中的代码,初始化MyClass的实例,并将其赋值给my_instance变量。 返回模块对象。
-
输出:Initializing MyClass instance。
后续导入:
-
当你再次导入x.py时(例如在file2.py中),Python会执行以下步骤:
检查sys.modules字典,发现x模块已经存在。 直接返回缓存的模块对象,不会重新执行x.py中的代码。
-
输出:42。
总结
Python通过模块缓存机制确保模块级别的实例只会被加载一次。具体步骤包括:
模块缓存:使用sys.modules字典缓存已经导入的模块。
模块查找路径:使用sys.path列表确定模块的查找路径。
模块加载:首次导入时,创建模块对象并执行模块代码。
后续导入:直接返回缓存的模块对象,不会重新执行模块代码。
这种机制确保了模块级别的实例在内存中是唯一的,避免了重复初始化和资源浪费。