动态加载类中的类标识v.s.导入

时间:2018-04-15 03:14:46

标签: python metaprogramming

这是我正在努力的事情的最小再现。这是使用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加载的对象被标识为&#39; test.Test&#39;,而不是&#39; test.Test&#39;的实例或子类。我使用import创建的类?有没有办法以编程方式检查它们是否属于同一个类,还是因为实例化的上下文不同而不可能?

1 个答案:

答案 0 :(得分:2)

  

为什么我们使用importlib加载的对象(标识为test.Test)不是我使用import创建的test.Test类的实例或子类?

一个班级是&#34;只是&#34;元类的一个实例。导入系统通常会阻止类对象多次实例化:类通常在模块范围内定义,如果已导入模块,则现有模块仅用于后续导入语句。因此,对同一个类的不同引用最终都指向生活在同一内存位置的相同类对象。

通过使用exec_module,你阻止了这个&#34;缓存命中&#34;在sys.modules 中,强制再次执行类声明,并在内存中创建一个新的类对象。

issubclass没有像对源类代码进行深入检查那样聪明,它或多或少只是寻找身份(CPython的实现here,快速跟踪完全匹配以及支持ABCs

的一些复杂情况
  

有没有办法以编程方式检查它们是否属于同一个类,还是因为实例化的上下文不同而不可能?

他们不是同一个班级。虽然源代码相同,但它们存在于不同的内存位置。你不需要exec_module的并发症来看这个,顺便说一下,有更简单的方法可以强制重新使用&#34;相同的&#34;类:

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

或者,在函数块中定义类并从函数调用中返回它。或者,使用三参数版本的type(name, bases, dict)从相同的源代码创建类。 isinstance检查(CPython实现here)很简单,不会检测到这些误导。