当前位置: 首页 > 知识库问答 >
问题:

比较:import语句与__import_函数

陆文博
2023-03-14

作为在正常情况下使用内置__import__()问题的后续,我进行了一些测试,并遇到了令人惊讶的结果。

我在这里比较了传统的< code>import语句和对< code>__import__内置函数的调用的执行时间。为此,我在交互模式下使用以下脚本:

import timeit   

def test(module):    
    t1 = timeit.timeit("import {}".format(module))
    t2 = timeit.timeit("{0} = __import__('{0}')".format(module))
    print("import statement:   ", t1)
    print("__import__ function:", t2)
    print("t(statement) {} t(function)".format("<" if t1 < t2 else ">"))

与链接的问题一样,这里是导入sys以及其他一些标准模块时的比较:

>>> test('sys')
import statement:    0.319865173171288
__import__ function: 0.38428380458522987
t(statement) < t(function)

>>> test('math')
import statement:    0.10262547545597034
__import__ function: 0.16307580163101054
t(statement) < t(function)

>>> test('os')
import statement:    0.10251490255312312
__import__ function: 0.16240755669640627
t(statement) < t(function)

>>> test('threading')
import statement:    0.11349136644972191
__import__ function: 0.1673617034957573
t(statement) < t(function)

到目前为止,import__import__()更快。这对我来说很有意义,因为正如我在链接的帖子中所写的那样,我发现与CALL_FUNCTION相比,当后者导致对__IMPORT_的调用时,对IMPORT_NAME指令进行优化是合乎逻辑的。

但是当涉及到不太标准的模块时,结果正好相反:

>>> test('numpy')
import statement:    0.18907936340054476
__import__ function: 0.15840019037769792
t(statement) > t(function)

>>> test('tkinter')
import statement:    0.3798560809537861
__import__ function: 0.15899962771786136
t(statement) > t(function)

>>> test("pygame")
import statement:    0.6624641952621317
__import__ function: 0.16268579177259568
t(statement) > t(function)

执行时间差异背后的原因是什么?import语句在标准模块上更快的实际原因是什么?另一方面,为什么__import_函数与其他模块相比更快?

测试从Python 3.6开始

共有3个答案

徐奇逸
2023-03-14

执行时间的差异背后的原因是什么?

导入语句有一个非常简单的路径。它导致IMPORT_NAME调用import_name并导入给定的模块(如果没有覆盖名称__import__):

dis('import math')
  1           0 LOAD_CONST               0 (0)
              2 LOAD_CONST               1 (None)
              4 IMPORT_NAME              0 (math)
              6 STORE_NAME               0 (math)
              8 LOAD_CONST               1 (None)
             10 RETURN_VALUE

另一方面,__import__通过所有函数通过CALL_FUNCTION执行的泛型函数调用步骤:

dis('__import__(math)')
  1           0 LOAD_NAME                0 (__import__)
              2 LOAD_NAME                1 (math)
              4 CALL_FUNCTION            1
              6 RETURN_VALUE

当然,它是内置的,比普通的py函数快得多,但它仍然比带有import_nameimport语句慢。

这就是为什么它们之间的时间差是恒定的。使用@MSeifert片段(纠正了不公平的时间:-)并添加另一个打印,您可以看到以下内容:

import timeit   

def test(module):    
    exec("import {}".format(module))
    t2 = timeit.timeit("{0} = __import__('{0}')".format(module))
    t1 = timeit.timeit("import {}".format(module))
    print("import statement:   ", t1)
    print("__import__ function:", t2)
    print("t(statement) {} t(function)".format("<" if t1 < t2 else ">"))
    print('Diff: {}'.format(t2-t1))


for m in sys.builtin_module_names:
    test(m)

在我的机器上,它们之间有大约0.17的恒定差异(通常预期有轻微差异)

*值得注意的是,这些并不完全等价。__import__不做任何名称绑定,正如字节码所证明的那样。

闽经纬
2023-03-14

请记住,所有模块在第一次导入后都会缓存到< code>sys.modules中,因此...

无论如何,我的结果如下所示:

#!/bin/bash

itest() {
    echo -n "import $1: "
    python3 -m timeit "import $1"
    echo -n "__import__('$1'): "
    python3 -m timeit "__import__('$1')"
}

itest "sys"
itest "math"
itest "six"
itest "PIL"
  • 导入系统: 0.481
  • __import__('sys'): 0.586
  • 导入数学:0.163
  • __import__('数学'): 0.247
  • 导入六: 0.157
  • __import__('六'): 0.273
  • 导入PIL: 0.162
  • __import__('PIL'): 0.265
窦宏旷
2023-03-14

timeit 测量总执行时间,但模块的第一次导入(无论是通过导入还是__import__)都比后续导入慢 - 因为它是唯一实际执行模块初始化的导入。它必须在文件系统中搜索模块的文件,加载模块的源代码(最慢)或以前创建的字节码(慢但比解析.py文件快一点)或共享库(对于 C 扩展),执行初始化代码,并将模块对象存储在 sys.modules 中。后续导入将跳过所有这些并从 sys.modules 中检索模块对象。

如果颠倒顺序,结果将不同:

import timeit   

def test(module):    
    t2 = timeit.timeit("{0} = __import__('{0}')".format(module))
    t1 = timeit.timeit("import {}".format(module))
    print("import statement:   ", t1)
    print("__import__ function:", t2)
    print("t(statement) {} t(function)".format("<" if t1 < t2 else ">"))

test('numpy')
import statement:    0.4611093703134608
__import__ function: 1.275512785926014
t(statement) < t(function)

获得无偏差结果的最好方法是导入一次,然后进行计时:

import timeit   

def test(module):    
    exec("import {}".format(module))
    t2 = timeit.timeit("{0} = __import__('{0}')".format(module))
    t1 = timeit.timeit("import {}".format(module))
    print("import statement:   ", t1)
    print("__import__ function:", t2)
    print("t(statement) {} t(function)".format("<" if t1 < t2 else ">"))

test('numpy')
import statement:    0.4826306561727307
__import__ function: 0.9192819125911029
t(statement) < t(function)

所以,是的,导入总是比__import__快。

 类似资料:
  • 通过运行下面的代码,我可以直接与if语句进行比较,速度几乎是使用max函数的4倍。 我试图理解这背后的原因。 对比:0.63秒,最大:2.3秒

  • 每个spark程序都有这行。当我上网了解scala中“implicits”的用法时,我得到了这样的定义: 我明白这个定义,但它给我带来了以下的疑问。 其他导入语句不也是这样吗?每个import语句都会带来我们希望在代码中使用的函数/选项/方法。如果与常规导入语句不同,那么它们给表带来了什么?它们有何不同?谁能解释一下scala中参数和函数的真正用例吗?

  • 问题内容: 有人可以向我解释该声明的工作方式吗? 例如,我在包中有一个类型: 我的包裹中有一个类型: 这给了我以下错误: 我试图将导入更改为以下代码: 但是得到这个错误: 我也不明白。那么,和之间有什么区别?如何正确设置我的模型? 问题答案: 每个包都有一组类型,函数,变量等。我们将它们称为 实体 。每个实体可以导出(名称以大写字母开头),也可以不导出(名称以小写字母开头)。 一个包只能访问另一个

  • 如果你想要直接输入argv变量到你的程序中(避免在每次使用它时打sys.),那么你可以使用from sys import argv语句。如果你想要输入所有sys模块使用的名字,那么你可以使用from sys import *语句。这对于所有模块都适用。一般说来,应该避免使用from..import而使用import语句,因为这样可以使你的程序更加易读,也可以避免名称的冲突。

  • 问题内容: 在Python中使用IF语句时,必须执行以下操作才能使“层叠”正常工作。 检查“等于”时,是否有办法使Python接受多个值?例如, 问题答案: if job in (“mechanic”, “tech”): print “awesome” elif job in (“tool”, “rock”): print “dolt” 括号中的值是一个元组。该运营商检查,看是否右手柄元组内的某处