On Feb 27, 3:50 pm, "Ziga Seilnacht" wrote:Andrew Felch wrote:
Hello all,
I'm using the metaclass trick for automatic reloading of class member
functions, found at:http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/160164
My problem is that if I
1) pickle an object that inherits from "AutoReloader"
2) unpickle the object
3) modify one of the pickled' object's derived class methods
4) reload the module holding the class
... then the changes don't affect the unpickled object. If I unpickle
the object again, of course the changes take effect.
My friend that loves Smalltalk is laughing at me. I thought I had the
upperhand when I discovered the metaclasses but now I am not sure what
to do. I really don't want to have to unpickle again, I'm processing
video and it can take a long time.
By the way, I used to avoid all of these problems by never making
classes, and always building complex structures of lists,
dictionaries, and tuples with global functions. It's going to take me
a while to kick those horrible habits (during my transition, I'm
deriving from list, dict, etc. hehe), perhaps a link to the metaclass
trick is in order in the tutorial's comments on reload?
Any help that avoids having to unpickle again is appreciated!
Thanks,
Andrew Felch
This happens because unpickling doesn't recreate your object by
calling its type. MetaInstanceTracker registers an instance only
when it is created by calling a class.
You can solve this by moving the instance registration to
AutoReloader.__new__ and using pickle protocol version 2, but the
best solution is to avoid both pickle (old pickles break if you
change your code) and autoreloading (it's meant to be used in
interactive console and entertaining ircbots, not in normal code).
Ziga- Hide quoted text -
- Show quoted text -
Thanks Ziga. I use pickle protocol 2 and binary file types with the
command: "cPickle.dump(obj, file, 2)"
I did your suggestion, i commented out the "__call__" function of
MetaInstanceTracker and copied the text to the __new__ function of
AutoReloader (code appended). I got a crazy recursive error message
(also appended below). In my code, I am creating a new instance,
rather than using the pickled object (it needs to work in both modes).
Thanks very much for helping me get through this. With my development
approach, finding a solution to this problem is really important to
me.
...
File "C:\Python25\lib\site-packages\tdbu.py", line 67, in __new__
instance = super(MetaInstanceTracker, self).__call__(*args, **kw)
File "C:\Python25\lib\site-packages\tdbu.py", line 67, in __new__
instance = super(MetaInstanceTracker, self).__call__(*args, **kw)
File "C:\Python25\lib\site-packages\tdbu.py", line 67, in __new__
instance = super(MetaInstanceTracker, self).__call__(*args, **kw)
File "C:\Python25\lib\site-packages\tdbu.py", line 67, in __new__
instance = super(MetaInstanceTracker, self).__call__(*args, **kw)
File "C:\Python25\lib\site-packages\tdbu.py", line 67, in __new__
instance = super(MetaInstanceTracker, self).__call__(*args, **kw)
File "C:\Python25\lib\site-packages\tdbu.py", line 67, in __new__
instance = super(MetaInstanceTracker, self).__call__(*args, **kw)
RuntimeError: maximum recursion depth exceeded
<code>
import weakref, inspect
class MetaInstanceTracker(type):
def __new__(cls, name, bases, ns):
t = super(MetaInstanceTracker, cls).__new__(cls, name, bases,
ns)
t.__instance_refs__ = []
return t
def __instances__(self):
instances = [(r, r()) for r in self.__instance_refs__]
instances = filter(lambda (x,y): y is not None, instances)
self.__instance_refs__ = [r for (r, o) in instances]
return [o for (r, o) in instances]
## def __call__(self, *args, **kw):
## instance = super(MetaInstanceTracker, self).__call__(*args,
**kw)
## self.__instance_refs__.append(weakref.ref(instance))
## return instance
class InstanceTracker:
__metaclass__ = MetaInstanceTracker
class MetaAutoReloader(MetaInstanceTracker):
def __new__(cls, name, bases, ns):
new_class = super(MetaAutoReloader, cls).__new__(
cls, name, bases, ns)
f = inspect.currentframe().f_back
for d in [f.f_locals, f.f_globals]:
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
for subcls in old_class.__subclasses__():
newbases = ()
for base in subcls.__bases__:
if base is old_class:
newbases += (new_class,)
else:
newbases += (base,)
subcls.__bases__ = newbases
break
return new_class
class AutoReloader:
__metaclass__ = MetaAutoReloader
def change_class(self, new_class):
self.__class__ = new_class
def __new__(self, *args, **kw):
instance = super(MetaInstanceTracker, self).__call__(*args,
**kw)
self.__instance_refs__.append(weakref.ref(instance))
return instance
Thanks,
Andrew Felch