模块A从B中导入C类 A有一个'run'程序,除其他外创建一个C实例。 第一次运行后,模块B更新而不退出A;然后完成第二次运行。 新的C实例是来自B的更新版本还是原始版本?
答案 0 :(得分:0)
这取决于您对 update 的概念:
请记住,Python是一种编译语言:读取模块并将其编译为字节代码。因此,当您更改源文件时,没有任何反应,因为代码已经编译。
只需重复导入模块也无效,导入器只需检查sys.modules
并从那里返回已存在的模块。只有在加载未知模块时才会触发模块 load (根据sys.modules
)。
也没有自动检查已更改的源文件,因此源文件在更改后不会自动重新编译。
但是,编译文件(.pyc
,.pyo
)在使用之前会根据其源文件进行检查。如果相应的源文件较新或具有different size,则会对新负载进行重新编译(不在导入时,在加载上)。但请注意,.pyc
时间戳分辨率仅 32位,因此实际文件系统时间戳会被截断。
你可以跳过一些严肃的箍,让python导入一个更改的源文件:
sys.path.insert(0, os.getcwd())
with open("B.py", 'w') as bfile:
bfile.write("""
class B:
def act(self):
print "First version"
""")
import B
first_class = B.B()
with open("B.py", 'w') as bfile:
bfile.write("""
class B:
def act(self):
print "Second version"
""")
try:
os.unlink('B.pyc')
except OSError:
pass
try:
os.unlink('B.pyo')
except OSError:
pass
reload(B)
second_class = B.B()
first_class.act()
second_class.act()
这实际上是巧合,但代码显示了重新导入和重新编译时出现的一个真正问题。因为两个B.py
是一个接一个地快速创建的,所以它们的时间戳通常比较等于.pyc
时间戳。你可能会很幸运并点击实际的时间戳翻转,但这只是运气。由于Unix上的文件大小也相同(请注意第二个版本的额外换行符),因此文件大小检查也会报告两个源文件相同。
如果您删除了unlink()
操作,那么 - 大部分时间 - 都会重新编译 no 模块。相反,您会从B
文件中获取.pyc
的版本版本,即使它不再与B.py
匹配。
在任何情况下,都会保留初始导入的代码对象。在此示例中,first_class
来自B.py
的初始版本,second_class
来自更新版本。第一个类已经编译成字节代码,在内存中,它不会因为你改变它的源文件而改变。
出于所有实际目的,这两个类来自不同的模块,偶然碰巧具有相同的源文件。
这可能只对调试很有用,我强烈建议不要将它用于任何有效的工作。如果您的模块具有多个单个源文件,则尤其如此。
那就是说,{3}在Python 3中不再存在,因为即使在Python 2中也没有按预期工作。
答案 1 :(得分:0)
不,它可能会使用原件。当您从C
中的B
导入课程A
时,您将在A
中创建对名为C
的对象(恰好是一个类)的引用。
如果您不重新分配到C
,它仍然会引用相同的对象,因此,除非您在更新B
期间实际修改了同一个对象,否则更改将不会通过使用A
明显地从C
开始。
现在举一些你可能做过的例子:
如果您只是编辑B
的源代码(在以下代码中的input
之后)并且具有以下python代码:
from B import C
c1 = C()
input("Modify B!")
c2 = C()
然而,就A
而言,你还没有修改任何东西,它甚至没有费心去查看你修改过的来源。
现在让我们尝试重新导入:
from B import C
c1 = C()
input("Modify B!")
from B import C
c2 = C()
现在你重新分配给C,但是python仍然不愿意看你的修改。第二个导入的作用是首先检查它是否已加载B
模块,它有,然后它只是抓取C
并放入A
命名空间。
现在让我们尝试一下:
import B
from B import C
c1 = C()
input("Modify B!)
reload(B)
c2 = C()
然后仍然没有运气,reload(B)
只告诉口译员重新加载模块,所以现在B
引用了新模块,但C
在这个过程中没有更新
现在离你最近的地方:
import B
from B import C
c1 = C()
input("Modify B!")
reload(B)
from B import C
c2 = C()
现在c2 = C()
将使用修改后的B
中的类定义,但要注意c1
确实使用了旧定义,它的类型仍然是旧版本班级C
。
最后,当我提到实际修改同一个对象时,我想我会给出一个例子:
class C:
pass
c = C()
def fubar(self):
return 42
C.fubar = fubar
c.fubar()
这里发生的是首先定义类C
并创建一个对象。然后我通过添加一个方法来修改类C
。 c
的类型没有改变,它是同一个类,但是现在该类已经创建了c
之后的新方法。