最近在编写一个自动化应用,需要管理浏览器的状态。
通过单例模式的设计,实现了只有一个浏览器实例,这样其它模块或者函数调用这个浏览器类,用的都是同一个实例,就可以管理这个浏览器的状态了。
类似下面这样调用:
def manual_test_main():
manual_test_special_func()
manual_test_browser_open()
def manual_test_special_func():
from tests.web.browser import Browser
browser = Browser()
browser.open_browser()
def manual_test_browser_open():
from web.browser import Browser
browser = Browser()
print(browser, browser.is_browser_open())
if __name__ == "__main__":
manual_test_main()
你能从上面看出问题吗?
编辑器没有报错,导入的都是 web 目录下的 browser.py 中的 Browser 类,因为 Browser 是单例模式,理论上来说,Browser 实例化时,应该是同一个模块才对。
但是在测试中,即使浏览器打开了,获取到的浏览器状态还是关闭的,我测试了半天,也没想清楚到底是哪里出了问题,难道是我的单例模式失效了??
直到我尝试把两个 browser 对象都打印出来,才发现了不同:
注意看,不管是它们的 id,还是对象的路径都有区别,也就是说,它们是不同的实例对象。
那么就有两个问题:
- 不同的路径,为什么可以调用同一个模块
- 路径不同,即使相同的模块也会不同吗?
这里其实就暴露了我对 Python 知识的匮乏,根据我之后的了解,这两个问题有了答案:
1. 为什么不同的路径,可以调用同一个对象
这里涉及到 Python 模块搜索路径的问题。
sys.path
是一个包含所有模块搜索路径的列表。在 Python 启动时,它会被初始化并包含以下路径:
- 当前工作目录:
sys.path
的第一个元素是运行 Python 程序时的当前工作目录。你可以使用os.getcwd()
查看当前工作目录。 - 标准库目录:Python 标准库的路径。这些路径取决于 Python 的安装位置和版本。
- 第三方包目录:安装的第三方包所在的目录,例如使用
pip
安装的包。 PYTHONPATH
环境变量:如果设置了PYTHONPATH
环境变量,其内容会被添加到sys.path
中。这个环境变量可以用来添加额外的路径。.pth
文件:如果存在.pth
文件(通常位于 site-packages 目录下),这些文件的内容也会被添加到sys.path
中。.pth
文件用于添加额外的路径到 Python 的模块搜索路径。
因为我是从 app 目录下启动的,app 目录作为当前工作目录,已经被添加到sys.path
了,那么我省略掉 app 自然也就无所谓了。
2. 路径不同,即使相同的模块也会不同吗?
即使模块内容相同,Python 将每个模块的文件路径视为不同的模块。因此,它们会被视为不同的模块实例,每个实例在内存中有不同的 id
。
所以即使我使用了单例模式,事实上,它们也是不同的实例。