Reload Tricks

Michael Spencer mahs at telcopartners.com
Sat Jan 22 06:24:47 CET 2005


Kamilche wrote:
> I want my program to be able to reload its code dynamically. I have a
> large hierarchy of objects in memory. The inheritance hierarchy of
> these objects are scattered over several files.
> 
> I find that after reloading the appropriate files, and overwriting the
> __class__ of object instances, one more thing is necessary: reloading
> the __bases__ of each reloaded class. If I don't do this, the modules
> reloaded first point to old versions of the classes from later modules,
> and when the later module is reloaded, it doesn't update the
> inheritance hierarchy of classes already loaded.
> 
> This appears to be working... but now I'm wondering, what else did it
> not change? Can I expect more toes to be blown off?
> 
> --Kamilche
> 

There are some cases when re-assigning __class__ isn't possible, for example:
  >>> class A(object):
  ...     pass
  ...
  >>> class B(dict):
  ...     pass
  ...
  >>> class C:
  ...     pass
  ...
  >>> a = A()
  >>> a.__class__ = B
Traceback (most recent call last):
   File "<input>", line 1, in ?
TypeError: __class__ assignment: 'A' object layout differs from 'B'
  >>> a.__class__ = C
Traceback (most recent call last):
   File "<input>", line 1, in ?
TypeError: __class__ must be set to new-style class, not 'classobj' object
  >>>

An alternative approach (with some pros and cons) is to modify the class in 
place, using something like:

  >>> def reclass(cls, to_cls):
  ...     """Updates attributes of cls to match those of to_cls"""
  ...
  ...     DONOTCOPY = ("__name__","__bases__","__base__",
  ...                     "__dict__", "__doc__","__weakref__")
  ...
  ...     fromdict = cls.__dict__
  ...     todict = to_cls.__dict__
  ...
  ...     # Delete any attribute present in the new class
  ...     [delattr(cls,attr) for attr in fromdict.keys()
  ...         if not((attr in todict) or (attr in DONOTCOPY)) ]
  ...
  ...     for to_attr, to_obj in todict.iteritems():
  ...
  ...         if to_attr in DONOTCOPY:
  ...             continue
  ...
  ...         # This overwrites all functions, even if they haven't changed.
  ...         if type(to_obj) is types.MethodType:
  ...             func = to_obj.im_func
  ...             to_obj = types.MethodType(func,None, cls)
  ...
  ...         setattr(cls, to_attr,to_obj)
  ...
  >>> class A(object):
  ...     attr = "A"
  ...
  >>> class B(object):
  ...     attr = "B"
  ...
  >>> a = A()
  >>> reclass(A,B)
  >>> a.attr
'B'
  >>>

This copies attributes of old and new-style classes (in fact anything with a 
__dict__ so probably a module would work too)

You still run into problems trying to re-assigning __bases__ to incompatible 
objects, but this one-attribute-at-a-time approach gives you the potential to 
intercept problem cases.  In the example above, problems are avoided by not 
copying __bases__.

An additional advantage of this aprpoach is that you don't need to keep track of 
class instances, in order to change their __class__.  Instances automatically 
acquire the new behavior

One wart is that class docstrings are not writeable, so cannot be copied.  Why?

Michael




More information about the Python-list mailing list