如何在Python中重新加载类对象的方法代码?

5 投票
4 回答
7170 浏览
提问于 2025-04-16 09:50

我在找一种方法,可以在程序运行时重新加载一个类对象的方法。这里有个例子:

我首先定义了一个类A,这个类在文件test.py里。

class A:
    def __init_(self):
        pass
    def Message(self):
        print "1"

然后我在Linux中启动了Python的命令行,执行了以下代码:

>>> from test import A
>>> a = A()
>>> a.Message()
1

现在我在运行时打开test.py文件,修改了方法“Message”:

class A:
    def __init_(self):
        pass
    def Message(self):
        print "2"

但是当我在Python命令行中执行a.Message()时,结果总是“1”,而不是“2”。

我该怎么写代码,让对象'a.Message'执行更新后的代码呢?

非常感谢!

chu

4 个回答

2

你需要从新的代码中重新加载你的模块,获取类并把它重新分配给实例:

import sys
def update_class(instance):
  cls=instance.__class__
  modname=cls.__module__
  del sys.modules[modname]
  module=__import__(modname)
  instance.__class__=getattr(module,cls.__name__)

在开发和更新代码时,你可以使用同样的方法来定义实例,这样它们在每次使用时都会重新加载它们的类。

import sys

class ObjDebug(object):
  def __getattribute__(self,k):
    ga=object.__getattribute__
    sa=object.__setattr__
    cls=ga(self,'__class__')
    modname=cls.__module__ 
    mod=__import__(modname)
    del sys.modules[modname]
    reload(mod)
    sa(self,'__class__',getattr(mod,cls.__name__))
    return ga(self,k)

class A(ObjDebug):
   pass

a=A()

如果你修改了A的代码,添加了一个方法,那么你可以直接使用这个方法,而不需要重新创建一个新的实例a。

实际上,del sys.modules[modname] 似乎会导致一些麻烦的错误,导致某些符号在一瞬间变成了None对象。(具体原因不太明白)

3

我刚花了一段时间自己解决这个问题,最后得到了一个和这里其他人建议的稍微不同的解决方案。我把它放在了一个gist上,并加了很多注释,不过按照StackOverflow的风格,我在这里简单解释一下:

与其去修改旧版本类的每一个实例,或者把它们复制到新版本类的新实例中,不如直接把旧类的方法(也就是类本身的属性,而不是类的实例)替换成新类的更新方法。这样做还可以处理多个版本类之间的继承关系。

4

要在交互式解释器中重新加载你的模块,可以使用

import test
reload(test)
from test import A

但是,这样做不会影响已经存在的 A 实例——它们仍然是旧类型的 A,所以还是会使用旧的方法。我觉得改变已经存在的实例是不可能的,也不是个好主意。

顺便提一下,我推荐使用 IPython 来测试代码。它可以方便地使用 %reload 这个魔法命令进行递归加载,更有用的是可以用 %run 这个魔法命令来测试模块。

撰写回答