导入时,为什么即使只指定了特定部分,整个模块也会加载?

Zac*_*aro 5 python

我正在努力控制我的程序内存占用.我以为我会从导入开始,因为我只使用相当大的PyObjC库中的3-4个函数.但是,我有点惊讶地发现,导入较大模块的特定部分与实际加载到内存中的内容无关.

Memory Profiler输出

在OSX上加载整个Quartz.CoreGraphics库:

Line #    Mem usage    Increment   Line Contents
================================================
    77                             @profile 
    78     7.953 MB     0.000 MB   def test_import_all():
    79    26.734 MB    18.781 MB    import Quartz.CoreGraphics as CG
Run Code Online (Sandbox Code Playgroud)

它以近19MB的速度拉入整个图书馆.

试图只提取我需要的东西,得到相同的19MB结果:

Line #    Mem usage    Increment   Line Contents
================================================
    82                             @profile
    83     7.941 MB     0.000 MB   def test_import_some():
    84    26.727 MB    18.785 MB    from Quartz.CoreGraphics import CGImageGetWidth 
Run Code Online (Sandbox Code Playgroud)

因此,似乎特定的导入与实际加载的内容无关.

从一个巨大的模块只需要一小部分功能似乎是一个常见的用例.有没有办法将我需要的模块加载到内存中,或者这只是使用外部库的结果?

Ste*_*sop 5

这就是模块加载的工作原理.运行时维护一组已加载的模块,因此即使您只导入了几个符号,也可以访问整个模块.这有两个令人满意的后果:

  • 未来同一模块的进口速度很快.
  • 模块级别的任何具有副作用的代码只执行一次(禁止模块重新加载),而不是执行可变次数,具体取决于模块在哪些位置导入.

这也是相当不可避免的,如果你考虑到模块中的任何功能,可直接利用来自它的名称或间接地通过访问模块命名空间,globals(),sys.modules[__name__],eval,或什么的.因此,除非进行一些巧妙的优化以证明特定函数不执行此操作(Python实现通常不会这样做),整个模块命名空间必须在内存中.