This module is used in the implementation of the bytecodehack library; if you're actually writing bytecodehacks you should be able to ignore it's presence.
This less-elegant-than-it-should-probably-be module allows me to seem to give the code attribute to CodeStrings and the function attribute to the EditableCode object, i.e. the attributes that reach back up the containment heirachy, without creating circular references. Circular references are the bane of large object systems in Python as its refcounting scheme means that circular references almost always lead to memory leaks.
The rationale behind this module is that when an attribute is being stored in the body of its parent, it has no need to refer to its parent. This reference can be added in whenever a reference to the attribute is made, and a new object with this attribute added returned. Confused? A diagram might help, and courtesy of the rather remarkable dia program, here are some.
If the constructor of EditableCode behaved like this:
class EditableCode: def __init__(self): self.co_code = CodeString() self.co_code.code = self
code = EditableCode()
results in a data structure looking roughly like this:
The cyclic reference is obvious, so when the reference to codegoes out of scope, code and code.co_code refer mutually to each other, and so the storage they occupy will not be reclaimed.
In this simple scheme, a reference to "code.co_code" just points into another part of the structure:
What the EditableCode contructor actually does is more like:
from bytecodehacks.fleshy import Flesher class EditableCode(Flesher): def __init__(self): self.co_code = CodeString() self.make_fleshed("co_code",code=Flesher.MagicSelfValue)
code = EditableCode()
results in a structure that looks like this:
and executing "code.co_code" now triggers the magic Flesher.__getattr__ method which clones the co_code attribute, adds a code attribute to the clone (I refer to this process as ``fleshing'', for no particular reason) and returns it. This leads to a data structure looking like this:
No cyclic references! Magic! This last diagram also indicates the most serious problem with this approach - a new object is created with each attribute access.
You must arrange for __init__ to be called. If you get tracebacks like:
Traceback (innermost last): File "<stdin>", line 1, in ? File "/home/mwh21/src/python/bytecodehacks/fleshy.py", line 19, in __getattr__ dict = self.__dict__[fleshable_dict_name] KeyError: $ forgot to call __init__ did we? $
then you haven't.
It is in my opinion one of the suckiest areas of Python, that it is possible by carelessness to fail to initialise a base class part of a derived object. I'd be upset if it wasn't possible to do this deliberately (this module wouldn't work, for starters) but I think it should have to be an act of will.
After all the sweat and toil writing this (and considering the unpleasant mess this makes of performance), I think I'd like to see some form of garbage collection in Python. This won't happen any time soon, but never mind.
Send comments to email@example.com