In a program I'm working on, I need to do a substantial amount of real-time reflection in order to maintain a list of known "attributes" throughout the program structure (for use by a sort of "virtual programming assistant"). I came up with kind of a "cheaty" way of doing this (which I'm frankly kind of proud of), which is essentially akin to the following:
old_getattribute = obj.__getattribute__
def new_getattribute(self, attr):
# various things...
return old_getattribute(attr)
methods = {"__getattribute__" : new_getattribute}
obj.__class__ = type( # Dynamically create a new class
"%s" % obj.__class__.__name__, # whose name is _CLASS
(obj.__class__, ), # which subclasses CLASS
methods) # and uses the new __getattribute__
Essentially, I dynamically create a new type that subclasses the original type, using a new __getattribute__
method, and then reassign the object's internal __class__
to this new dynamic type.
As amazed and happy as I am that this works (in Python >=3.6 at least), I'm still on the fence about using it. In my past python programming, any kind of modification of an object's magic methods from outside the object's class
definition was pretty much forbidden, and yet here I am doing it twice in really messed up ways.
I would like to use it, as it would save me a ton of time instead of keeping track of individual objects in some "appropriate" manner and then continually checking them for changes, but I want to be sure that I'm not going to create any potential danger for the rest of the program.
So that's what I'm here to ask about. From a software architecture standpoint, how potentially dangerous is this method, and what pitfalls I may encounter getting this to work? I'm sure there are more than a few OOP/general programming principles I'm severely violating with this, but I'm willing to let those slide if this can be "managed".
__dict__
to see what's changed, the change is received immediately.__getattribute__
?__getattribute__
and the call to the original__getattribute__
. Otherwise it's essentially the same as just decorating__getattribute__
dynamically. The reason I have to do theobj.__class__ = type(...)
trick is because magic method lookups are performed on the class, and not on the instance, so settingobj.__getattribute__ = decorator(obj.__getattribute__)
doesn't work.__class__
is being modified, but I can't really see any major problems that could occur. Also, as I'm the one running the injection code above, I can essentially control when it's called and ensure it's never called in a parallel process.