关于stripe-python中提前导入stripe.app_info以填充sys.modules的导入逻辑疑问
嘿,这个问题得结合Python的模块导入机制和库的向后兼容思路来拆解,我给你一步步理清楚:
先把你提到的代码段放出来方便对照:
# We must import the app_info module early to populate it into # `sys.modules`; otherwise doing `import stripe.app_info` will end up # importing that module, and not the global `AppInfo` name from below. import stripe.app_info from stripe._app_info import AppInfo as AppInfo from stripe._version import VERSION as VERSION
你的疑问特别合理:AppInfo明明已经移到内部模块stripe._app_info里了,为什么还要提前导入看起来无关的stripe.app_info?
其实核心是Python的模块缓存机制加上库的向后兼容需求,具体来说:
Python模块导入的基本逻辑
当你第一次导入一个模块时,Python会把这个模块对象存入sys.modules字典做缓存,后续任何地方再导入这个模块,都会直接从缓存里取,不会重新去磁盘加载文件或执行模块代码。stripe-python的兼容考量
大概率Stripe团队之前的版本里,AppInfo是放在stripe.app_info模块中的,后来为了优化代码结构,把它移到了内部模块stripe._app_info(下划线开头是库内部专用模块,不建议用户直接依赖)。但为了不让旧代码(比如用户写的from stripe.app_info import AppInfo)直接报错,他们保留了stripe.app_info这个模块,里面应该做了兼容处理——比如用延迟导入的方式,当用户访问stripe.app_info.AppInfo时,自动从stripe的全局命名空间里取我们在__init__.py中导入的AppInfo。提前导入的必要性
如果不提前把stripe.app_info导入并填充到sys.modules里,就可能触发循环导入错误:- 假设后续有代码(库内部或用户代码)执行
from stripe.app_info import AppInfo,Python会去加载stripe/app_info.py文件。 - 而这个兼容模块需要从
stripe的全局命名空间取AppInfo,但如果此时__init__.py还没执行到from stripe._app_info import AppInfo as AppInfo这一行,就会出现“找不到AppInfo”的错误。
提前导入
stripe.app_info就是先把这个模块缓存到sys.modules里,后续任何导入操作都直接用缓存的模块,既避免了循环导入的问题,也确保旧代码的导入写法能正常工作,属于防御性编程手段,防止因为导入顺序不同导致的奇怪bug。- 假设后续有代码(库内部或用户代码)执行
备注:内容来源于stack exchange,提问作者Pacific East

