我对Python中import module
和from module import name1, name2 ...
之间的区别有疑问。我是Python的新手(上周),在Windows-64上使用Python 3.6。
在Python Tutorial中,对这两种import
方法进行了简短的讨论。据说不建议使用from module import *
,因为存在污染当前命名空间的危险。但是,没有迹象表明import module
和from 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的评论之后 这些是我的问题
答案 0 :(得分:1)
在这种情况下,全局变量iGlobalA作为ModuleA的副本导入
不。在from ModuleA import iGlobalA
之后,ModuleA.iGlobalA
和ModuleY.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#import和https://docs.python.org/3/reference/import.html,但您确实必须了解“在本地名称空间中定义名称”和“存储对该值的引用”在本地名称空间“真的意味着。
这也意味着设计库模块的人需要指定应该如何导入该模块。
这里的问题是lib设计糟糕(如果它使用全局变量, 确实设计得很糟糕)或者(至少)你试图访问的名称不是(或者不应该是lib的API的一部分。
我是否遗漏了文档中的内容?
可能也是,我恐怕我已经习惯了这项工作如何记住我是如何第一次学习它的
所有平台的这种行为是否相同? 这是未来可以依赖的预期行为吗?
是的,是的。这实际上是语言规范的一部分,改变它几乎可以打破所有现有代码。