Python是否检测导入模块的更新

时间:2015-10-06 14:44:31

标签: python python-module

模块A从B中导入C类 A有一个'run'程序,除其他外创建一个C实例。 第一次运行后,模块B更新而不退出A;然后完成第二次运行。 新的C实例是来自B的更新版本还是原始版本?

2 个答案:

答案 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并创建一个对象。然后我通过添加一个方法来修改类Cc的类型没有改变,它是同一个类,但是现在该类已经创建了c之后的新方法。