Class 动态加载中的标识 类 v.s。 类 通过导入加载

Class Identification in dynamically loaded classes v.s. classes loaded by import

这是我正在处理的内容的最小复制。这是使用 Python 3.6.5:

sample.py:

import importlib.util
import inspect

from test import Test

t = Test()

spec = importlib.util.spec_from_file_location('test', './test.py')
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)

loaded_test = None
for name, obj in inspect.getmembers(module):
    if inspect.isclass(obj):
        loaded_test = obj

print(type(t))
print(loaded_test)
print(isinstance(t, loaded_test))
print(issubclass(t.__class__, loaded_test))

test.py(在同一目录下):

class Test(object):
    pass

运行 此代码将为您提供以下输出:

<class 'test.Test'>
<class 'test.Test'>
False
False

那么为什么我们使用importlib加载的对象,标识为'test.Test',而不是我创建的'test.Test' class的实例或子class使用导入?有没有办法以编程方式检查它们是否相同 class,或者是否不可能因为它们实例化的上下文不同?

Why is the object that we load using importlib, which is identified as test.Test, not an instance or subclass of the test.Test class I created using import?

A class 是 "just" 元 class 的实例。导入系统通常会阻止 class 对象被多次实例化:classes 通常是在模块范围内定义的,如果模块已经被导入,则现有模块只会被重复用于后续的导入语句.因此,对同一 class 的不同引用最终都指向位于同一内存位置的相同 class 对象。

通过使用 exec_module 你在 sys.modules 中阻止了这个 "cache hit",强制再次执行 class 声明,以及要在内存中创建的新 class 对象。

issubclass 并没有像深入检查 class 源代码那样聪明,它或多或少只是在寻找身份(CPython 的实现 here, with a fast-track for exact match and some complications for supporting ABCs

Is there a way to programmatically check if they're the same class, or is it impossible because the context of their instantiation is different?

它们不一样class。尽管源代码相同,但它们存在于不同的内存位置。你不需要 exec_module 的复杂性来看到这个,顺便说一下,有更简单的方法来强制重新创建 "same" class:

>>> import test
>>> t = test.Test()
>>> isinstance(t, test.Test)
True
>>> del sys.modules['test']
>>> import test
>>> isinstance(t, test.Test)
False

或者,在函数块中定义 class 并从函数调用中定义 return。或者,使用 type(name, bases, dict). The isinstance check (CPython implementation here) 的三参数版本从相同的源代码创建 classes 很简单,并且不会检测到这些误导。