Python中'import as'和变量赋值的区别

6
以下两个语句有何不同,每个语句的后果是什么?
导入为:
from module.submodule import someclass as myclass

将值赋给变量:

from module.submodule import someclass
myclass = someclass

实际上是一样的。 - Joseph Seung Jae Dollar
1
有一些差别,检查字节码。 - o11c
一样的事情,你正在将它分配给当前脚本全局空间中的一个变量。 - Ja8zyjits
PyCharm 无法在我使用第二种方法(变量赋值)时显示快速定义或转到源代码,但当我使用第一种方法时它可以正常工作。 - Yogesh Mangaj
2个回答

5
这里提供的字节码输出是针对Python 3.4的,但生成字节码的代码应该适用于任何版本,并且相同的基本原则也适用。
测试工具:
from dis import dis

def compile_and_dis(src):
    dis(compile(src, '<string>', 'exec'))

案例一:

>>> compile_and_dis('from module.submodule import someclass as myclass')
  1           0 LOAD_CONST               0 (0)
              3 LOAD_CONST               1 (('someclass',))
              6 IMPORT_NAME              0 (module.submodule)
              9 IMPORT_FROM              1 (someclass)
             12 STORE_NAME               2 (myclass)
             15 POP_TOP
             16 LOAD_CONST               2 (None)
             19 RETURN_VALUE

这是唯一一种方法,只向当前的locals()(即__dict__,对于在模块中定义的所有内容将变为globals())中添加一个名称(myclass)。而且,这也是最短的字节码。

如果在module.submodule中找不到someclass,则此方法会引发ImportError。然而,它将尝试将module.submodule.someclass作为模块加载。

情况2:

>>> compile_and_dis('from module.submodule import someclass; myclass = someclass')
  1           0 LOAD_CONST               0 (0)
              3 LOAD_CONST               1 (('someclass',))
              6 IMPORT_NAME              0 (module.submodule)
              9 IMPORT_FROM              1 (someclass)
             12 STORE_NAME               1 (someclass)
             15 POP_TOP
             16 LOAD_NAME                1 (someclass)
             19 STORE_NAME               2 (myclass)
             22 LOAD_CONST               2 (None)
             25 RETURN_VALUE

这与案例1几乎完全相同,只是它向本地命名空间泄漏了第二个名称(someclass)。如果导入和赋值不是连续的,你就有理论上重用名称来指代其他变量的风险,但如果你在重叠使用变量名,那么你的设计一定糟糕透顶。
请注意字节码中无用的STORE_NAME/LOAD_NAME循环(围绕着一个无关的POP_TOP)。
案例3:
>>> compile_and_dis('from module import submodule; myclass = submodule.someclass')
  1           0 LOAD_CONST               0 (0)
              3 LOAD_CONST               1 (('submodule',))
              6 IMPORT_NAME              0 (module)
              9 IMPORT_FROM              1 (submodule)
             12 STORE_NAME               1 (submodule)
             15 POP_TOP
             16 LOAD_NAME                1 (submodule)
             19 LOAD_ATTR                2 (someclass)
             22 STORE_NAME               3 (myclass)
             25 LOAD_CONST               2 (None)
             28 RETURN_VALUE

这种方法会将submodule泄漏到本地命名空间中。如果未找到该类,则不会引发ImportError,而是在赋值过程中引发AttributeError。它不会尝试将module.submodule.someclass作为模块加载(实际上甚至不关心module.submodule是否为模块)。

情况4:

>>> compile_and_dis('import module.submodule as submodule; myclass = submodule.someclass')
  1           0 LOAD_CONST               0 (0)
              3 LOAD_CONST               1 (None)
              6 IMPORT_NAME              0 (module.submodule)
              9 LOAD_ATTR                1 (submodule)
             12 STORE_NAME               1 (submodule)
             15 LOAD_NAME                1 (submodule)
             18 LOAD_ATTR                2 (someclass)
             21 STORE_NAME               3 (myclass)
             24 LOAD_CONST               1 (None)
             27 RETURN_VALUE

这类似于第3种情况,但需要module.submodule是一个模块。
第5种情况:
>>> compile_and_dis('import module.submodule; myclass = module.submodule.someclass')
  1           0 LOAD_CONST               0 (0)
              3 LOAD_CONST               1 (None)
              6 IMPORT_NAME              0 (module.submodule)
              9 STORE_NAME               1 (module)
             12 LOAD_NAME                1 (module)
             15 LOAD_ATTR                2 (submodule)
             18 LOAD_ATTR                3 (someclass)
             21 STORE_NAME               4 (myclass)
             24 LOAD_CONST               1 (None)
             27 RETURN_VALUE

这种方法与第4种情况类似,尽管2个属性加载在不同的位置,因此不同的变量泄漏到本地命名空间。


1
这非常有帮助! - Yogesh Mangaj

4
在您的变量赋值示例中,主要区别在于 someclass 仍然作为一个名称可用。当导入具有相同名称的类型时,这将导致问题。
from datetime import datetime
from arrow import datetime  # problem!

为了解决此问题:
from datetime import datetime
from arrow import datetime as arrow_datetime

有任何想法为什么PyCharm不能显示someclass.some_attribute_or_method的快速定义或转到源代码,当我使用第二种方法(变量赋值)时,但是当我使用第一种方法时它工作得很好? - Yogesh Mangaj

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接