Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Python, how do you change an instantiated object after a reload?

Let's say you have an object that was instantiated from a class inside a module. Now, you reload that module. The next thing you'd like to do is make that reload affect that class.

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

Do you have to make a new ClassChange object, copy myObject into that, and delete the old myObject? Or is there a simpler way?

Edit: The run() method seems like a static class style method but that was only for the sake of brevity. I'd like the run() method to operate on data inside the object, so a static module function wouldn't do...

like image 924
user129975 Avatar asked Jul 03 '09 20:07

user129975


People also ask

How do you unset an object in Python?

You cannot manually destroy objects in Python, Python uses automatic memory management. When an object is no longer referenced, it is free to be garbage collected, in CPython, which uses reference counting, when a reference count reaches zero, an object is reclaimed immediately.

What happens when you instantiate an object in Python?

In short, Python's instantiation process starts with a call to the class constructor, which triggers the instance creator, . __new__() , to create a new empty object. The process continues with the instance initializer, . __init__() , which takes the constructor's arguments to initialize the newly created object.

How do you instantiate an object in Python?

Instantiating a class in Python is simple. To instantiate a class, we simply call the class as if it were a function, passing the arguments that the __init__ method defines. The return value will be the newly created object.

How do you reimport in Python?

1 Answer. You can re-import a module in python, by using the importlib and its function reload.


1 Answers

To update all instances of a class, it is necessary to keep track somewhere about those instances -- typically via weak references (weak value dict is handiest and general) so the "keeping track" functionality won't stop unneeded instances from going away, of course!

You'd normally want to keep such a container in the class object, but, in this case, since you'll be reloading the module, getting the old class object is not trivial; it's simpler to work at module level.

So, let's say that an "upgradable module" needs to define, at its start, a weak value dict (and an auxiliary "next key to use" int) with, say, conventional names:

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

Each class in the module must have, typically in __init__, a call _register(self) to register new instances.

Now the "reload function" can get the roster of all instances of all classes in this module by getting a copy of _objs before it reloads the module.

If all that's needed is to change the code, then life is reasonably easy:

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)

Alas, one typically does want to give the new class a chance to upgrade an object of the old class to itself more finely, e.g. by a suitable class method. That's not too hard either:

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)

For example, say the new version of class Zap has renamed an attribute from foo to bar. This could be the code of the new 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

This is NOT all -- there's a LOT more to say on the subject -- but, it IS the gist, and the answer is WAY long enough already (and I, exhausted enough;-).

like image 192
Alex Martelli Avatar answered Nov 15 '22 20:11

Alex Martelli