以某种方式可以*live*修改Python代码(比如在Lisp或Erlang中)

Rab*_*ski 10 lisp python erlang

我想知道是否有可能在某种程度上修改Python代码,同时保留所有实例化对象和方法的状态,就像我认为可能在Lisp或Erlang(*)中?

说,我有一个活跃的Python会话,我foo从自编写的模块中实例化了这个类:

class foo():
   @classmethod
   def do_something(self):
       print "this is good"
Run Code Online (Sandbox Code Playgroud)

Python命令行:

>>> f =foo()
>>> f.do_something()
Run Code Online (Sandbox Code Playgroud)

现在,我想将print语句更改为其他内容(例如print "this is better").如果我编辑我的模块文件,并重新加载它,我必须重新实例化该f对象.有没有办法能够f.do_something()再次打电话而不必先打个电话f=foo()

所以,我必须这样做:

>>> reload my_module
>>> f =foo()
>>> f.do_something() # with changed print statement
Run Code Online (Sandbox Code Playgroud)

但我想这样做:

>>> reload my_module
>>> f.do_something() # with changed print statement
Run Code Online (Sandbox Code Playgroud)

(*)我基于酷炫的Erlang电影以及来自Practical Common Lisp的这个片段来说这个陈述:" 当这个小虫出现在距离地球1亿英里的荒野中时 - 团队能够诊断和修复正在运行的代码,让实验完成."

编辑:我一直在想更多关于这一点,也许我想要的是因为应用于OO本质上存在缺陷(即,类和方法的状态如何).我认为Erlang允许这样做,因为据我所知,它更多的是关于单独的通信对象,因此实时更新对象的代码更有意义.我不确定,所以仍然可以回答.

编辑2:描述我想要的最好的方法可能是在下面的帖子中重述我在评论中所说的内容:"调用时,方法必须指向新的方法定义/位置."

jon*_*tar 6

是的你可以,也很简单.你想只改变实例f,而不是类foo,对吗?

>>> class foo():
   @classmethod
   def do_something(self):
       print "this is good"


>>> f = foo()
>>> f.do_something()
this is good
>>> def new_ds():
        print "this is better"


>>> f.do_something = new_ds
>>> f.do_something()
    this is better

>>> f2 = foo()
>>> f2.do_something() #unchanged
    this is good
Run Code Online (Sandbox Code Playgroud)

编辑

由于范围的变化,这几乎肯定不太理想,但是在重新加载后立即发生了这样的变化

testmod.py - 最初

class foo():
   @classmethod
   def do_something(self):
       outside_func()

def outside_func():
    print "this is good"
Run Code Online (Sandbox Code Playgroud)

testmod.py - 更改后

class foo():
   @classmethod
   def do_something(self):
       outside_func()

def outside_func():
    print "this is better"
Run Code Online (Sandbox Code Playgroud)

翻译员

>>> import testmod
>>> f = testmod.foo()
>>> f.do_something()
this is good
>>> reload(testmod)
<module 'testmod' from 'C:\Python26\testmod.py'>
>>> f.do_something()
this is better
Run Code Online (Sandbox Code Playgroud)

  • 现在,您只需在重新加载模块时自动执行此操作. (7认同)
  • 他希望模块包含的所有内容都已更改 (3认同)

Ros*_*ron 5

您可以创建类装饰器或元类,以确保在重新加载类时更改旧对象的类.这是一个工作(至少对我来说)的例子,虽然我不建议你按原样使用它,但是用它作为灵感来创造符合你的意图和需求的东西.(它也没有在没有定义的类上测试__init__,所以要小心.)

import sys
import weakref

class _ObSet(weakref.WeakValueDictionary):
    def add(self, ob):
        self[id(ob)] = ob

    def remove(self, ob):
        del self[id(ob)]

    def __iter__(self):
        return self.itervalues()

def reloadable(cls):

    # Change the __init__ of the old class to store the instances
    # in cls.__instances (you might stick this into a class as a
    # static method to avoid name collisions)

    if '__init__' in vars(cls):
        old_init = vars(cls)['__init__']

        def __init__(self, *a, **kw):
            self.__class__.__instances.add(self)
            old_init(self, *a, **kw)

        cls.__init__ = __init__

    elif '__new__' in vars(cls):
        old_new = vars(cls)['__new__']

        def __new__(cls, *a, **kw):
            self = old_new(cls, *a, **kw)
            cls.__instances.add(self)
            return self

        cls.__new__ = __new__

    else:

        def __init__(self, *a, **kw):
            self.__class__.__instances.add(self)
            super(cls, self).__init__(*a, **kw)

        cls.__init__ = __init__

    cls.__instances = _ObSet()

    module = sys.modules.get(cls.__module__)
    if module is None:
        return cls

    old_cls = getattr(module, cls.__name__, None)
    if old_cls is None:
        return cls

    # Change the bases of all subclasses of the old class
    for ob in old_cls.__instances:
        if ob.__class__ is old_cls:
            ob.__class__ = cls

    # Change the class of all instances of the old class
    for child_cls in old_cls.__subclasses__():
        child_cls.__bases__ = tuple(cls if base is old_cls else base
                                    for base in child_cls.__bases__)

    return cls
Run Code Online (Sandbox Code Playgroud)

以下是如何使用它的示例:

from reloading import reloadable

@reloadable
class A(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b


class B1(A):
    def __init__(self, c, *a):
        super(B1, self).__init__(*a)
        self.c = c

@reloadable
class B2(A):
    def __init__(self, c, *a):
        super(B2, self).__init__(*a)
        self.c = c
Run Code Online (Sandbox Code Playgroud)

然后它是如何工作的:

>>> import test_reload
>>> a = test_reload.A(1, 2)
>>> b1 = test_reload.B1(1, 2, 3)
>>> b2 = test_reload.B2(1, 4, 6)
>>> isinstance(a, test_reload.A)
True
>>> isinstance(b1, test_reload.A)
True
>>> isinstance(b1, test_reload.B1)
True
>>> isinstance(b2, test_reload.A)
True
>>> isinstance(b2, test_reload.B2)
True
>>> reload(test_reload)
<module 'test_reload' from 'test_reload.pyc'>
>>> isinstance(a, test_reload.A)
True
>>> isinstance(b1, test_reload.A)
True
>>> isinstance(b1, test_reload.B1) # will fail, not @reloadable
False
>>> isinstance(b2, test_reload.A)
True
>>> isinstance(b2, test_reload.B2)
True
>>> a.a, a.b
(1, 2)
>>> b1.a, b1.b, b1.c
(2, 3, 1)
>>> b2.a, b2.b, b2.c
(4, 6, 1)
Run Code Online (Sandbox Code Playgroud)