How to load new class definitions at runtime?

Michael Hudson mwh at python.net
Fri Nov 26 06:40:27 EST 2004


aleaxit at yahoo.com (Alex Martelli) writes:

> SM <sjmaster at gmail.com> wrote:
> 
> > aleaxit at yahoo.com (Alex Martelli) wrote in message
> news:<1gn8ja3.7d3sbhuk94khN%aleaxit at yahoo.com>...
> > > If you need [classes] to be _updatable_ at runtime, look at a recipe by
> > > Michael Hudson in the cookbook -- I've updated it for the printed
> > > version 2nd ed (since we do have __subclasses__ now, yay!) but that's
> > > not done yet.  My vote for most useful custom metaclasses ever...

Wow, thanks :)

> > That recipe is here:
> > http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/160164
> > 
> > Is it correct that this metaclass solution does not handle changes to
> > __init__, including new attributes that are bound there? I.e., it
> 
> It doesn't rerun __init__ nor any other method on existing instances.

Well... it runs change_class on existing instances, but see below...

> > basically only deals with modification or addition or deletion of
> > methods; if you need to add new attributes or have any other new code
> > in __init__, you'll have to make new instances for all the old
> > instances, or deal with it some other way. In which case would you be
> > better off just using that other way from the start?
> 
> It's pretty trivial to enhance the recipe to run a per-instance or
> per-subclass __make_old_into_new__ method, if that method exists, on all
> existing old instances -- if you need to adjust the old instances'
> states (==attributes), that strikes me as the best architecture.

I'd sort of thought about this when I wrote the recipe initially --
it's why there is a change_class method -- but now realize that it
doesn't really work, because when you reload the module, it runs the
*old* change_class when it would (obviously) be much more useful to
somehow run a new method.

How about this diff (from what's on the recipe page now):

*** autoreloader.py~	2004-10-27 17:50:03.000000000 +0100
--- autoreloader.py	2004-11-26 11:36:45.000000000 +0000
***************
*** 27,33 ****
              if d.has_key(name):
                  old_class = d[name]
                  for instance in old_class.__instances__():
!                     instance.change_class(new_class)
                      new_class.__instance_refs__.append(
                          weakref.ref(instance))
                  # this section only works in 2.3
--- 27,34 ----
              if d.has_key(name):
                  old_class = d[name]
                  for instance in old_class.__instances__():
!                     instance.__class__ = new_class
!                     instance.update_self()
                      new_class.__instance_refs__.append(
                          weakref.ref(instance))
                  # this section only works in 2.3
***************
*** 44,51 ****
  
  class AutoReloader:
      __metaclass__ = MetaAutoReloader
!     def change_class(self, new_class):
!         self.__class__ = new_class
  
  class Bar(AutoReloader):
      pass
--- 45,52 ----
  
  class AutoReloader:
      __metaclass__ = MetaAutoReloader
!     def update_self(self):
!         pass
  
  class Bar(AutoReloader):
      pass
***************
*** 67,69 ****
--- 68,94 ----
      b2.meth(2)
      # new Baz() instances now play too:
      Baz().meth(3)
+ 
+ class C(AutoReloader):
+     def __init__(self, a):
+         self.a = 1
+     def get_attribute(self):
+         return self.a
+ 
+ c = C(1)
+ 
+ print c.get_attribute()
+ 
+ class C(AutoReloader):
+     def __init__(self, b):
+         self.b = b
+     def get_attribute(self):
+         return self.b
+     
+     def update_self(self):
+         self.b = self.a
+         del self.a
+ 
+ print c.get_attribute()
+ 
+         

Probably a bit more useful, though it has the disadvantage that it
will crash and burn if you forget to remove the update_self() method
before reloading again (that's probably inevitable, though).  Maybe
you could introduce revision numbers on the instances and the classes
or something, though this might be getting a little complex.

Cheers,
mwh

-- 
  Important data should not be entrusted to Pinstripe, as it may
  eat it and make loud belching noises.
   -- from the announcement of the beta of "Pinstripe" aka. Redhat 7.0



More information about the Python-list mailing list