В Python, как вы изменяете экземпляр объекта после перезагрузки?
Скажем, у вас есть объект, который был создан из класса внутри модуля.
Теперь вы перезагружаете этот модуль.
Следующее, что вы хотели бы сделать, это заставить эту перезагрузку повлиять на этот класс.
mymodule.py
---
class ClassChange():
def run(self):
print 'one'
myexperiment.py
---
import mymodule
from mymodule import ClassChange # why is this necessary?
myObject = ClassChange()
myObject.run()
>>> one
### later, i changed this file, so that it says print 'two'
reload(mymodule)
# trick to change myObject needed here
myObject.run()
>>> two
Вам нужно создать новый объект ClassChange, скопировать myObject в это и удалить старый объект myObject? Или есть более простой способ?
Изменить: метод run() выглядит как метод статического класса, но это было только ради краткости. Я бы хотел, чтобы метод run() работал с данными внутри объекта, поэтому статическая функция модуля не выполнялась бы...
Ответы
Ответ 1
Чтобы обновить все экземпляры класса, необходимо отслеживать где-то об этих экземплярах - обычно через слабые ссылки (слабое значение dict является самым удобным и общим), поэтому функциональность "отслеживания" не останавливает ненужные экземпляры из конечно, уезжаю!
Обычно вы хотите сохранить такой контейнер в объекте класса, но в этом случае, поскольку вы будете перезагружать модуль, получение старого объекта класса не является тривиальным; проще работать на уровне модуля.
Итак, скажем, что "обновляемый модуль" должен определить с самого начала слабое значение dict (и вспомогательную "следующую клавишу для использования" int "), например, с обычными именами:
import weakref
class _List(list): pass # a weakly-referenceable sequence
_objs = weakref.WeakValueDictionary()
_nextkey = 0
def _register(obj):
_objs[_nextkey] = List((obj, type(obj).__name__))
_nextkey += 1
Каждый класс модуля должен иметь, как правило, __init__
, вызов _register(self)
для регистрации новых экземпляров.
Теперь функция перезагрузки может получить список всех экземпляров всех классов в этом модуле, получив копию _objs
, прежде чем перезагружать модуль.
Если все, что нужно, это изменить код, тогда жизнь достаточно проста:
def reload_all(amodule):
objs = getattr(amodule, '_objs', None)
reload(amodule)
if not objs: return # not an upgraable-module, or no objects
newobjs = getattr(amodule, '_objs', None)
for obj, classname in objs.values():
newclass = getattr(amodule, classname)
obj.__class__ = newclass
if newobjs: newobjs._register(obj)
Увы, обычно обычно требуется дать новому классу возможность обновить объект старого класса до более тонко, например. методом подходящего класса. Это тоже не слишком сложно:
def reload_all(amodule):
objs = getattr(amodule, '_objs', None)
reload(amodule)
if not objs: return # not an upgraable-module, or no objects
newobjs = getattr(amodule, '_objs', None)
for obj, classname in objs:
newclass = getattr(amodule, classname)
upgrade = getattr(newclass, '_upgrade', None)
if upgrade:
upgrade(obj)
else:
obj.__class__ = newclass
if newobjs: newobjs._register(obj)
Например, скажем, новая версия класса Zap переименовала атрибут из foo в bar. Это может быть код нового Zap:
class Zap(object):
def __init__(self):
_register(self)
self.bar = 23
@classmethod
def _upgrade(cls, obj):
obj.bar = obj.foo
del obj.foo
obj.__class__ = cls
Это НЕ ВСЕ - еще БОЛЬШЕ сказать по этому вопросу, но, это суть, и ответ уже достаточно длинный (и я, исчерпал достаточно; -).
Ответ 2
Вам нужно создать новый объект. Невозможно магически обновить существующие объекты.
Прочтите reload
встроенную документацию - это очень ясно. Здесь последний абзац:
Если модуль создает экземпляры класса, перезагрузка модуля, который определяет класс, не влияет на определения методов экземпляров - они продолжают использовать старое определение класса. То же самое верно для производных классов.
В документации есть другие оговорки, поэтому вы действительно должны прочитать их и рассмотреть альтернативы. Возможно, вы хотите начать новый вопрос с почему, который вы хотите использовать reload
, и попросить другие способы достижения того же.
Ответ 3
Мой подход к этому заключается в следующем:
- Просмотрите все импортированные модули и перезагрузите только те, у которых есть новый .py файл (по сравнению с существующим .pyc файлом).
- Для каждой перезагруженной функции и класса задайте значение old_function.__ code__ = new_function.__ code__.
- Для каждого перезагруженного класса используйте gc.get_referrers для отображения экземпляров класса и установки их атрибута __class__ в новую версию.
Преимущества этого подхода заключаются в следующем:
- Обычно нет необходимости перезагружать модули в определенном порядке
- Обычно требуется перезагрузить модули с измененным кодом и не более
- Не нужно изменять классы, чтобы отслеживать их экземпляры.
Здесь вы можете прочитать о технике (и ее ограничениях):
http://luke-campagnola.blogspot.com/2010/12/easy-automated-reloading-in-python.html
И вы можете скачать код здесь:
http://luke.campagnola.me/code/downloads/reload.py
Ответ 4
Вы должны получить новый класс из нового модуля и назначить его экземпляру.
Если вы можете запускать эту операцию в любое время, когда вы используете экземпляр с этим mixin:
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)
Ответ 5
Следующий код делает то, что вы хотите, но , пожалуйста, не используйте его (по крайней мере, пока вы не уверены, что поступаете правильно), Я отправляя его только для объяснения.
mymodule.py:
class ClassChange():
@classmethod
def run(cls,instance):
print 'one',id(instance)
myexperiment.py:
import mymodule
myObject = mymodule.ClassChange()
mymodule.ClassChange.run(myObject)
# change mymodule.py here
reload(mymodule)
mymodule.ClassChange.run(myObject)
Когда в вашем коде вы создаете myObject
, вы получаете экземпляр ClassChange
. Этот экземпляр имеет метод экземпляра, называемый run
. Объект сохраняет этот метод экземпляра (по причине, объясненной nosklo) даже при перезагрузке, поскольку перезагрузка перезагружает только класс ClassChange
.
В моем коде выше run
- это метод класса . Методы класса всегда связаны и работают над классом, а не с экземпляром (поэтому их первый аргумент обычно называется cls
, а не self
). Wenn ClassChange
перезагружается, так же как и этот метод класса.
Вы можете видеть, что я также передаю экземпляр в качестве аргумента для работы с правильным (таким же) экземпляром ClassChange. Вы можете видеть это, потому что один и тот же идентификатор объекта печатается в обоих случаях.
Ответ 6
Я не уверен, что это лучший способ сделать это, или сетки с тем, что вы хотите сделать... но это может сработать для вас. Если вы хотите изменить поведение метода, для всех объектов определенного типа... просто используйте функциональную переменную. Например:
def default_behavior(the_object):
print "one"
def some_other_behavior(the_object):
print "two"
class Foo(object):
# Class variable: a function that has the behavior
# (Takes an instance of a Foo as argument)
behavior = default_behavior
def __init__(self):
print "Foo initialized"
def method_that_changes_behavior(self):
Foo.behavior(self)
if __name__ == "__main__":
foo = Foo()
foo.method_that_changes_behavior() # prints "one"
Foo.behavior = some_other_behavior
foo.method_that_changes_behavior() # prints "two"
# OUTPUT
# Foo initialized
# one
# two
Теперь вы можете иметь класс, отвечающий за перезагрузку модулей, и после перезагрузки установите Foo.behavior
на что-то новое. Я пробовал этот код. Он отлично работает: -).
Это работает для вас?
Ответ 7
Есть трюки, чтобы сделать то, что вы хотите.
Кто-то уже упоминал, что вы можете иметь класс, который хранит список его экземпляров, а затем меняет класс каждого экземпляра на новый при перезагрузке.
Однако это неэффективно. Лучший способ - изменить старый класс так, чтобы он был таким же, как новый класс.
Взгляните на старую статью, которую я написал, которая, вероятно, лучше всего подходит для перезагрузки, которая будет изменять старые объекты.
http://www.codexon.com/posts/a-better-python-reload