我知道在python中使用import *
是不好的形式,我不打算养成它的习惯。然而,我最近遇到了一些我不理解的奇怪行为,并想知道是否有人可以向我解释。
假设我有三个python脚本。第一个first_script.py
包含:
MESSAGE = 'this is from the first script'
def print_message():
print MESSAGE
if __name__ == '__main__':
print_message()
显然运行此脚本会给我MESSAGE的内容。我有一个名为second_script.py
的第二个脚本,包含:
import first_script
first_script.MESSAGE = 'this is from the second script'
if __name__ == '__main__':
first_script.print_message()
行为(打印this is from the second script
)对我有意义。我导入了first_script.py
,但是在其命名空间中覆盖了一个变量,所以当我调用print_message()
时,我得到了该变量的新内容。
但是,我也有third_script.py
,包括:
from first_script import *
MESSAGE = 'this is from the third script'
if __name__ == '__main__':
print MESSAGE
print_message()
这产生的第一行是可以理解的,但第二行对我来说没有意义。我的直觉是因为我在第一行通过*导入了我的主命名空间,所以我有一个全局变量
叫MESSAGES
。然后在第二行我覆盖MESSAGES
。那么为什么函数(从第一个脚本导入)产生OLD输出,特别是在second_script.py
的输出的情况下。有什么想法吗?
答案 0 :(得分:1)
这与Scope
有关。有关此问题的详细说明,请参阅Short Description of the Scoping Rules?
有关大量示例的详细分类,请参阅http://nbviewer.ipython.org/github/rasbt/python_reference/blob/master/tutorials/scope_resolution_legb_rule.ipynb
以下是您具体案例的详细信息:
要求从第三个测试文件调用的print_message
函数打印出一些MESSAGE
个对象。此函数将使用标准LEGB
分辨率顺序来标识此引用的对象。 LEGB
是指Local, Enclosing function locals, Global, Builtins
。
MESSAGES
函数中未定义print_message
。MESSAGE
模块的全局范围中定义的first_script
。然后解决方案停止,但我会将其他内容包含在内以便完整。 内置插件 - python内置插件列表found here。
因此,您可以看到变量MESSAGE
的分辨率将立即停止在Global
中,因为那里定义了一些内容。
我指出的另一个资源是Lexical scope vs Dynamic scope,这可以帮助您更好地了解范围。
HTH
答案 1 :(得分:0)
import module
,from module import smth
和from module import *
可以有不同的用例。
import tools
加载tools
模块,并在本地名称空间(也称为tools
)中添加对它的引用。之后,您可以通过在tools
之前加上tools.var1
来访问任何工具参考,例如import tools as sloot
变量:
sloot.var1
完全相同,但是您使用别名从模块访问引用(例如:import numpy as np
)。它主要用于具有众所周知的别名(例如from tools import foo
)的模块。
tools
直接从当前名称空间中的try:
from mod1 import foo
except ImportError:
from mod2 import foo
...
foo() # actually calls foo from mod1 if available else foo from mod2
模块导入一些符号。这意味着您只能使用指定的符号,因为它们不需要限定。一个很好的用例是,您可以从具有相同功能的不同模块中导入符号。例如
from tools import *
这通常是可移植性。
危险:
_
这是一个常见的习惯用法,但是如果该模块未提供文档,则可能无法达到您的期望。实际上,它从模块中导入所有公共符号,默认情况下,所有不包含首字母__all__
且可能包含不需要的内容的符号。此外,模块可以声明一个特殊变量__all__
,假定该变量声明了公共接口,在这种情况下,仅导入__all__ = ['foo', 'bar']
def baz(x):
return x * 2
def foo():
return baz('FOO')
def bar():
return baz('BAR')
中包含的符号。
示例:
mod.py
from mod import *
print(foo()) # should print FOOFOO
# ERROR HERE
x = baz("test") # will choke with NameError: baz is not defined
您可以使用(假设可以访问mod.py)
import mod
print(mod.baz("test")) # will display as expected testtest
同时
from tools import *
因此,只有在tools
模块的文档中声明它是安全的并列出实际导入的符号时,才应使用#include <iostream>
using namespace std;
int main()
{
int* i;
i = (int*)0x7ffABCDDCBA1;
*i = 1;
cout << *i << " " << i << endl;
}
。
答案 2 :(得分:0)
直接分配更改对象的引用,但修改不更改。例如,
a = []
print(id(a))
a = [0]
print(id(a))
打印两个不同的ID,但是
a = []
print(id(a))
a.append(0)
print(id(a))
打印相同的ID。
在second_script.py
中,分配仅修改了first_script
,这就是first_script.py
和second_script.py
都可以找到{{1}的相同属性MESSAGE
的原因}。在first_script
中,直接分配更改了third_script.py
的引用;因此,在分配之后,MESSAGE
中的变量MESSAGE
与third_script.py
中的MESSAGE
是不同的变量。
考虑相关示例:
first_script.py
first_script.py
third_script.py
MESSAGE = ['this is from the first script']
def print_message():
print(MESSAGE)
if __name__ == '__main__':
print_message()
在这种情况下,from first_script import *
MESSAGE.append('this is from the third script')
if __name__ == '__main__':
print(MESSAGE)
print_message()
打印两条相同的消息,表明third_script.py
导入的名称仍然可以修改。