Python中的问题,导入模块和模块导入之间存在差异

时间:2018-03-26 11:11:49

标签: python python-import

我对Python中import modulefrom module import name1, name2 ...之间的区别有疑问。我是Python的新手(上周),在Windows-64上使用Python 3.6。

Python Tutorial中,对这两种import方法进行了简短的讨论。据说不建议使用from module import *,因为存在污染当前命名空间的危险。但是,没有迹象表明import modulefrom module import name1, name2..之间存在任何重大的操作差异,这意味着这是一个偏好或方便的问题。

然而,实践中似乎存在很大差异。考虑这个名为ModuleA的模块,定义一个全局变量和一个函数:

# ModuleA
iGlobalA = 0

def fA():
    iGlobalA += 1
    print( "MA: iGlobalA=", iGlobalA )

print( "Module A Initialised, iGlobalA=", iGlobalA )

使用import ModuleA创建一个单独的ModuleA命名空间。现在,该模块的成员可以作为命名空间的公共成员使用,如下所示:

import ModuleA as MA

def fX():
    print( "MX: Before, ModuleA.iGlobalA=", MA.iGlobalA )
    MA.fA()
    print( "MX: After 1, ModuleA.iGlobalA=", MA.iGlobalA )
    MA.fA()
    print( "MX: After 2, ModuleA.iGlobalA=", MA.iGlobalA )

fX()

这是输出:

MA: Initialised, iGlobalA= 100
MX: Before, ModuleA.iGlobalA= 100
MA: iGlobalA incremented to 101
MX: After 1, ModuleA.iGlobalA= 101
MA: iGlobalA incremented to 102
MX: After 2, ModuleA.iGlobalA= 102

完全符合预期。将此与使用from ModuleA import fA, iGlobalA形式的ModuleY进行对比,然后在没有限定条件的情况下引用ModuleA的这些成员:

# ModuleY
from ModuleA import fA, iGlobalA   

def fY():
    print( "MY: Before, ModuleA.iGlobalA=", iGlobalA )
    fA()
    print( "MY: After 1, ModuleA.iGlobalA=", iGlobalA )
    fA()
    print( "MY: After 2, ModuleA.iGlobalA=", iGlobalA )

fY()

在这种情况下,输出为:

MA: Initialised, iGlobalA= 100
MY: Before, ModuleA.iGlobalA= 100
MA: iGlobalA incremented to 101
MY: After 1, ModuleA.iGlobalA= 100
MA: iGlobalA incremented to 102
MY: After 2, ModuleA.iGlobalA= 100

在这种情况下,全局变量iGlobalA在ModuleA初始化后作为ModuleA 的副本导入,并成为与ModuleA.iGlobalA完全独立的变量。同样,函数fA作为对导入时定义的ModuleA 中函数的引用导入 - 如果函数在ModuleA中稍后重新分配,则导入模块中对fA()的引用保持不变,仅指向最初导入的函数。

我原本认为这些导入语法之间的差异应该在文档中更清楚地说明。这也意味着设计库模块的人需要指定应该如何导入该模块。

编辑 - 在@ abdullah-ahmed-ghaznavi的评论之后 这些是我的问题

  • 我是否遗漏了文档中的内容?
  • 所有平台的行为是否相同?
  • 这是未来可以依赖的预期行为吗?

1 个答案:

答案 0 :(得分:1)

  

在这种情况下,全局变量iGlobalA作为ModuleA的副本导入

不。在from ModuleA import iGlobalA之后,ModuleA.iGlobalAModuleY.iGlobalA都指向同一个对象 - 您可以通过在两个模块中打印id(iGlobalA)来检查这一点。现在虽然两个名称(最初)指向同一个对象,但名称本身是不同的 - 它们存在于不同的名称空间中(一个在ModuleA中,另一个在ModuleY中),所以,当fA()重新绑定名称iGlobalA时 - 实际上是ModuleA.iGlobalA - 只会影响ModuleA中的名称(因此此时两个名称都指向不同的对象)。

另一方面,当你在ModuleY中使用限定名ModuleA.iGlobalA时,你只有一个名字,所以当这个名字被{{1}反弹时(在ModuleA中)您在fA()中看到了更改,因为您确实在检查相同的名称

请注意,如果不是重新绑定一个名称,而是通过改变一个可变对象(即附加到一个列表,更新一个dict等)来尝试同样的事情,你就不会注意到行为的任何差异:

ModuleY

这里你需要理解的主要是what Python "variables" really are,而且Python没有真正的全局命名空间(“global”实际上意味着“模块级别”)。

  

我原本认为这些导入语法之间的差异应该在文档中更清楚地说明。

可能是的。请注意,实际上有一些关于整个事情的文档,cf https://docs.python.org/3/reference/simple_stmts.html#importhttps://docs.python.org/3/reference/import.html,但您确实必须了解“在本地名称空间中定义名称”和“存储对该值的引用”在本地名称空间“真的意味着。

  

这也意味着设计库模块的人需要指定应该如何导入该模块。

这里的问题是lib设计糟糕(如果它使用全局变量, 确实设计得很糟糕)或者(至少)你试图访问的名称不是(或者不应该是lib的API的一部分。

  

我是否遗漏了文档中的内容?

可能也是,我恐怕我已经习惯了这项工作如何记住我是如何第一次学习它的

  

所有平台的这种行为是否相同?   这是未来可以依赖的预期行为吗?

是的,是的。这实际上是语言规范的一部分,改变它几乎可以打破所有现有代码。