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