动态加载类与import加载类的类识别问题
回答
嘿,这个问题我之前也踩过坑,核心原因其实是同一个Python文件被当成了两个完全独立的模块,导致对应的类对象也不是同一个实例。
为什么会出现这种情况?
当你用from test import Test的时候,Python会把test.py加载成一个模块,并存到sys.modules的缓存里,键名是'test'。而你后面用importlib.util.spec_from_file_location加载时,虽然指定了模块名也是'test',但Python并没有复用缓存里已有的模块,而是重新读取文件、创建了一个全新的模块对象——里面的Test类自然也是一个全新的类,和你之前import的那个Test完全不是同一个对象。
你可以加两行代码验证这个结论:
import sys print(sys.modules['test'] is module) # 会输出False print(Test is loaded_test) # 也会输出False
既然两个Test是不同的类,那isinstance(t, loaded_test)返回False就不奇怪了,毕竟t是第一个Test类的实例。
怎么解决这个问题?
根据你的实际需求,有几种靠谱的方案:
1. 优先复用已加载的模块
如果不需要重新加载文件内容,直接用sys.modules缓存里的模块就行,避免重复加载:
import importlib.util import inspect import sys from test import Test t = Test() module_name = 'test' # 先检查缓存里有没有目标模块,有就直接用 if module_name in sys.modules: module = sys.modules[module_name] else: spec = importlib.util.spec_from_file_location(module_name, './test.py') module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) # 加载完存入缓存,避免后续重复加载 sys.modules[module_name] = module # 直接通过模块属性获取类,比遍历更精准 loaded_test = module.Test print(isinstance(t, loaded_test)) # 现在会返回True
2. 重新加载模块(适用于文件内容更新的场景)
如果你是因为test.py内容有修改,需要刷新模块,那可以用importlib.reload刷新已有的模块:
import importlib import sys from test import Test t = Test() # 重新加载已有的test模块 module = importlib.reload(sys.modules['test']) loaded_test = module.Test # 注意:旧实例t还是属于原来的Test类,如果模块里的Test有修改,isinstance会返回False # 这种方式适合用新类创建新实例的场景 new_t = loaded_test() print(isinstance(new_t, loaded_test)) # 返回True
3. 统一用动态加载的方式创建实例
如果你的场景就是要动态加载类,那干脆全程用动态加载的类来创建实例,别混合两种加载方式:
import importlib.util spec = importlib.util.spec_from_file_location('test', './test.py') module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) loaded_test = module.Test t = loaded_test() # 用动态加载的类创建实例 print(isinstance(t, loaded_test)) # 肯定返回True
内容的提问来源于stack exchange,提问作者Daniel Drexler
相关产品推荐
相关产品推荐

