Python 导入X和从X导入之间有什么区别*

Python 导入X和从X导入之间有什么区别*,python,python-3.x,Python,Python 3.x,给出的常见解释(如和)是来自X导入的* 使名称空间混乱,因此不建议使用。但是,下面的示例 这表明它不仅仅是混乱了名称空间 考虑以下代码: x1.py: g_c = 5 class TestClass(): def run(self): global g_c g_c = 1 print(g_c) # prints 1 x2.py: from x1 import * t = TestClass() t.run() print(g_c) # p

给出的常见解释(如和)是来自X导入的
*
使名称空间混乱,因此不建议使用。但是,下面的示例 这表明它不仅仅是混乱了名称空间

考虑以下代码:

x1.py:

g_c = 5
class TestClass():
    def run(self):
        global g_c
        g_c = 1
        print(g_c) # prints 1
x2.py:

from x1 import *
t = TestClass()
t.run()
print(g_c) # prints 5, why?
x3.py:

import x1
t = x1.TestClass()
t.run()
print(x1.g_c) # prints 1
运行
x2.py
x3.py
的结果是不同的(在python 3.6.8上)。有人能解释一下原因吗 这两个导入的行为不同

额外注释:(演示@tdelaney提到的关于列表的注释)

更改x1.py中的赋值,如下所示:

g_c = [5]
...
g_c.append(1)
from suman import *
print(display_name())

现在,x2.py和x3.py给出了相同的答案。只有在使用原子类型时才会出现问题。

当您导入X时,您必须使用
X
调用
X
中的函数,即:

X.func1()
但是,当您使用X import*中的
时,您将导入模块命名空间中所有不以
\uu
开头的名称或
模块中的所有名称,并且您不需要使用
X
来调用函数,即:

func1()

x2.py
中执行以下操作时:

from x1 import *
import x1
t = x1.TestClass()
t.run()
您刚刚从
x1
导入了所有内容,包括变量。这意味着您更改了当前脚本的命名空间(
x2
)。所以当你打电话时:

print(g_c)
它打印:

5
它是从当前脚本命名空间调用的。
在运行
x2
中的类
TestClass()
时,它更改了
x1
中g_c的值,但没有更改
x2
中的值

这表明了使用全局变量的缺点。
当变量被声明为全局变量时,它们将保留在内存中,直到程序执行完成。也就是说,您的全局声明仅在
x1
内有效。该声明在
x1
的命名空间中有效


当你这么做的时候:

from x1 import *
import x1
t = x1.TestClass()
t.run()
此导入
x1
作为一个模块,除非调用它,否则不会导入内部的所有内容。您确实调用了类
TestClass()
,并运行了它,结果是类内部的
g_c
成为一个全局变量。因此,对于
x1
g_c
1
,因为它是全局定义的,并且当前脚本不知道
x1
有一个
g_c=5
,因为它没有导入/调用。
print(x1.g_c)
上,它在x1的命名空间中调用g_c的值,即
1



让我们举一个例子,您将在Python中导入模块suman。它将整个模块带到您的工作区中,即在
import suman
语句中,它不允许您访问与like相关的任何方法或函数,例如,如果它们是suman模块中的函数
display\u name()
,则您必须像
suman.display\u name()

from suman import*
中,它将suman模块内的所有名称(例如
display()
等)带到您的模块中。现在,您可以访问这些名称,而无需模块名称前缀,如下所示:

g_c = [5]
...
g_c.append(1)
from suman import *
print(display_name())

from X import*
将X的全局命名空间中的对象重新绑定到当前模块的命名空间。如果重新分配了其中一个变量,则重新分配对当前命名空间是私有的

不过,导入不会更改导入的对象,它只是添加了一个新引用。例如,
classtestclass
的全局名称空间仍然是
x1.py
模块
TestClass.run
始终更改
x1.g_c
全局命名空间),无论哪个模块调用它

在x2.py中,将
x1.g_c
引用的原始
5
导入
x2.g_c
。但是重新分配
x1.g_c
不会影响
x2.g_c
,它是另一个命名空间中的变量


相反地,假设
x1.g_c
是一个列表,并且您的代码附加到该列表中。在这种情况下,所有模块都会看到附录,因为您正在修改由
x1.g_c
引用的对象,而不是重新分配它。

您可以想象
从x1 import*
执行的操作与

import m1
globals.update(m1.__dict__)
del m1
换句话说,它导入模块,然后将所有全局变量复制到您的命名空间中。但是,模块中的函数和类将继续引用它们的原始全局变量,而不是命名空间中的副本

注意上面的“实现”并不是完全正确的,因为并不是所有的名称都被复制,还有其他一些微妙之处,但我认为这是理解一般行为的一种简单方法

当模块更改其全局名称空间,但您看不到全局名称空间中反映的更改时,差异就变得明显


从x1导入执行
后*
变量
sys.modules[“x1”].g_c
g_c
是两个具有相同值的不同变量;模块中的代码指的是模块内部的代码,而不是您的副本。

此行为的关键如下:

  • Python为每个文件创建名称空间
  • global X
    使变量
    X
    引用位于声明
    global X
    的命名空间中的对象
  • 当在x2.py中执行
    t.run()
    时,将计算
    global g_c
    ,这使得
    g_c
    引用名称空间中x1.py而不是x2.py的对象

    尝试以下代码片段会有所帮助

    x2_uuy.py

    from x1 import *
    t = TestClass()
    t.run()
    print(g_c)  # prints 5
    
    import x1
    print(x1.g_c) # this print 1
    
    因为带星号的导入将分配给模块中的新变量,这些变量将导入到模块中。他们做两件不同的事情,他们的行为并没有什么不同。您可以将带星号的导入和来自模块导入的
    看作
    导入模块的糖;some_var=module.some_var;del模块
    对我来说,原因是