Hi, I am trying to create a mutable object for a persistent object database, and I've ran into a problem. The semantics are: Object gets created as a 'Swizzle'. Swizzle is a stub that intercepts any attempt to access object properties. When code tries to access any of swizzle's properties, the swizzle should automatically mutate into the stored object, by changing its class and properties. With the __set/get/del/attr__ traps this seemed feasible in Python. But it turns out to be trickier than I've thought, my naive implementation is not working. What happens is: swizzle.__getattr__() gets called calls swizzle.load() swizzle.__class__ = stored_class swizzle.__dict__ = stored_data calls getattr(self, attr_name) calls swizzle.__getattr__() gets called and I enter an infinite loop and blow the stack Since swizzle's __class__ and __dict__ have changed, shouldn't getattr use the new class to get attributes? Why this is puzzling is that if I call swizzle.load() directly (bypassing __getattr__ trap), and then try referencing swizzle's fields, everything works. The object mutates successfully, and __getattr__ never gets called again. Any hints on what might be going wrong? Hopefully others have struggled with the same problem before me. Details of Python's class implementation and method dispatch would also be interesting. I've read Guido's Unifying Types essay, but I think I lack Python history required for its full understanding. I've tried many different approaches: - Swizzle inheriting from object (so that I can call super methods directly). This one would not let me assign into the __class__ - Swizzle calling different combinations of self.__setattr/setattr/setattribute Here is my current code. Thanks in advance, Aleks class Swizzle(object): """ A swizzle is a placeholder for an object that has not been loaded. It mutates into the loaded object upon access TODO: locking """ def __init__(self, oid): self.__setattr__('oid', oid) def __getattr__(self, name): print "swizzle getattr called" # oid is the only attribute that does not cause a load if (name == 'oid'): return self.__dict__[name] self.load() return getattr(self,name) def __setattr__(self, name, value): """ setattr passes class & dict because these are called when we are mutating the object """ print "swizzle setattr called" if (name == '__class__' or name == '__dict__' or name == 'oid'): self.__dict__[name] = value return self.load() return setattr(self,name, value) def __delattr__(self, name): print "swizzle delattr called" self.load() return delattr(self, name) def load(self): """ TODO LOCK""" print "Trying to load ", self.oid RepositoryManager.manager.load(self) def RepositoryManager.load(anItem) anItem.__dict__ = getProperties(anItem.oid) anItem.__class__ = getClass(anItem.oid)
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4