Можно ли как-то * жить * модифицировать код Python (как в Лиспе или Эрланге) - PullRequest
10 голосов
/ 25 марта 2011

Мне было интересно, возможно ли каким-либо образом изменить живой код Python, сохраняя при этом все состояние созданных объектов и методов, как я думаю, возможно в Лиспе или Эрланге (*)?

Скажем, у меня есть активные сеансы Python, где я создал экземпляр класса foo из самописного модуля:

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

Командная строка Python:

>>> f =foo()
>>> f.do_something()

Теперь,Я хотел бы изменить оператор печати на что-то другое (например, print "this is better").Если я отредактирую файл модуля для этого и перезагрузлю его, мне придется заново создать экземпляр объекта f.Есть ли способ, чтобы можно было просто позвонить f.do_something() снова без необходимости сначала звонить f=foo()?

Итак, я должен сделать это:

>>> reload my_module
>>> f =foo()
>>> f.do_something() # with changed print statement

Но я хочусделать это:

>>> reload my_module
>>> f.do_something() # with changed print statement

(*) Я основываю это утверждение на классном фильме Эрланга и этом фрагменте из Practical Common Lisp : ' Когдаошибка, проявленная в дикой природе - в 100 миллионах миль от Земли - команда смогла диагностировать и исправить текущий код , что позволило завершить эксперименты. '

Редактировать: Я думал об этом немного больше, и, возможно, то, что я хочу, изначально ошибочно для применения к ОО (т. Е. Как насчет состояния класса и методов).Я думаю, что Эрланг допускает это, потому что, насколько я помню, речь идет больше об отдельных взаимодействующих объектах, поэтому актуальное обновление кода объекта имеет больше смысла.Хотя я не уверен, поэтому все еще открыт для ответов.

Edit2: Может быть, лучший способ описать то, что я хочу, это повторить то, что я сказал в комментарии в посте ниже: "Когдавызванные методы просто должны указывать на новые определения / местоположения методов. "

Ответы [ 3 ]

6 голосов
/ 25 марта 2011

Да, вы тоже можете довольно просто. Вы хотите изменить только экземпляр 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

EDIT

Это почти наверняка меньше, чем хотелось бы, из-за изменения объема, но такие изменения произошли для меня сразу после перезагрузки

testmod.py - изначально

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

def outside_func():
    print "this is good"

testmod.py - после изменения

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

def outside_func():
    print "this is better"

Переводчик

>>> 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
5 голосов
/ 25 марта 2011

Вы можете создать декоратор класса или метакласс, который гарантирует, что класс старых объектов изменяется при перезагрузке класса. Вот рабочий (по крайней мере для меня) пример, хотя я бы не советовал вам использовать его как есть, но использовать его как вдохновение для создания чего-то, что соответствует вашим намерениям и потребностям. (Это также не проверено на классах, которые не определяют __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

Вот пример того, как это используется:

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

А как это работает:

>>> 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)
0 голосов
/ 25 марта 2011

Это показывает, что вы можете изменить существующий класс и сделать эти изменения явными в экземплярах этого класса.Ключ заключается в том, чтобы изменить существующий класс, а не (заново) создать новый класс с тем же именем, что и у старого класса.

>>> class foo():
...     @classmethod
...     def do_something(self):
...         print "this is good"
... 
>>> f = foo()
>>> f.do_something()
this is good
>>> def do_something_else(self):
...     print "this is better"
... 
>>> foo.do_something = do_something_else
>>> f.do_something()
this is better
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...