使用*(星号)导入而不是作为python中的命名空间导入

时间:2014-11-05 21:17:36

标签: python namespaces

我知道在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的输出的情况下。有什么想法吗?

3 个答案:

答案 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

  1. 本地 - 此处MESSAGES函数中未定义print_message
  2. 封闭函数本地 - 没有函数包装此函数,因此跳过此操作。
  3. Global - 外部代码中任何显式声明的变量。它找到MESSAGE模块的全局范围中定义的first_script。然后解决方案停止,但我会将其他内容包含在内以便完整。
  4. 内置插件 - python内置插件列表found here

    因此,您可以看到变量MESSAGE的分辨率将立即停止在Global中,因为那里定义了一些内容。

  5. 我指出的另一个资源是Lexical scope vs Dynamic scope,这可以帮助您更好地了解范围。

    HTH

答案 1 :(得分:0)

import modulefrom module import smthfrom 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.pysecond_script.py都可以找到{{1}的相同属性MESSAGE的原因}。在first_script中,直接分配更改了third_script.py的引用;因此,在分配之后,MESSAGE中的变量MESSAGEthird_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导入的名称仍然可以修改。