Reputation: 10120
How do I replace a python object everywhere with another object?
I have two classes, SimpleObject
and FancyObject
. I've created a SimpleObject
, and have several references to it. Now I'd like to create a FancyObject
, and make all those references point to the new object.
a = SimpleObject()
some_list.append(a)
b = FancyObject()
a = b
is not what I want, it just changes what a points to. I read the following would work, but doesn't. I get an error "Attribute __dict__ is not writable":
a.__dict__ = b.__dict__
What I want is the equivalent of (pseudo-C):
*a = *b
I know this is hacky, but is there any way to accomplish this?
Upvotes: 15
Views: 14244
Reputation: 154
I encountered the same need and I worked around it using this trick:
class Reference:
directions = dict()
@staticmethod
def direct(reference): # -> Reference:
return Reference.directions.get(reference, reference)
@staticmethod
def route(origin, destination) -> None:
Reference.directions.update({origin: destination})
return None
def __init__(self) -> None:
self._reference = self
Reference.route(self, (lambda: self._reference))
return None
@property
def reference(self): # -> Reference:
return Reference.direct(self._reference)()
@reference.setter
def reference(self, obj) -> None:
Reference.route(self._reference, (lambda: obj.reference))
return None
class Example(Reference):
def __init__(self) -> None:
super().__init__()
return None
Which satisfies that the following:
obj1 = Example()
obj2 = Example()
obj3 = obj1
obj4 = Example()
print(obj1.reference is obj3.reference)
print(obj1.reference is obj2.reference)
print(obj3.reference is obj2.reference)
print()
obj1.reference = obj2.reference
print(obj1.reference is obj2.reference)
print(obj3.reference is obj2.reference)
print()
obj2.reference = obj4.reference
print(obj1.reference is obj4.reference)
print(obj2.reference is obj4.reference)
print(obj3.reference is obj4.reference)
outputs:
True
False
False
True
True
True
True
True
Upvotes: 0
Reputation: 3218
PyJack has a function replace_all_refs
that replaces all references to an object in memory.
An example from the docs:
>>> item = (100, 'one hundred')
>>> data = {item: True, 'itemdata': item}
>>>
>>> class Foobar(object):
... the_item = item
...
>>> def outer(datum):
... def inner():
... return ("Here is the datum:", datum,)
...
... return inner
...
>>> inner = outer(item)
>>>
>>> print item
(100, 'one hundred')
>>> print data
{'itemdata': (100, 'one hundred'), (100, 'one hundred'): True}
>>> print Foobar.the_item
(100, 'one hundred')
>>> print inner()
('Here is the datum:', (100, 'one hundred'))
Calling replace_all_refs
>>> new = (101, 'one hundred and one')
>>> org_item = pyjack.replace_all_refs(item, new)
>>>
>>> print item
(101, 'one hundred and one')
>>> print data
{'itemdata': (101, 'one hundred and one'), (101, 'one hundred and one'): True}
>>> print Foobar.the_item
(101, 'one hundred and one')
>>> print inner()
('Here is the datum:', (101, 'one hundred and one'))
Upvotes: 4
Reputation: 308402
Take advantage of mutable objects such as a list.
a = [SimpleObject()]
some_list.append(a)
b = FancyObject()
a[0] = b
Proof that this works:
class SimpleObject():
def Who(self):
print 'SimpleObject'
class FancyObject():
def Who(self):
print 'FancyObject'
>>> a = [SimpleObject()]
>>> a[0].Who()
SimpleObject
>>> some_list = []
>>> some_list.append(a)
>>> some_list[0][0].Who()
SimpleObject
>>> b = FancyObject()
>>> b.Who()
FancyObject
>>> a[0] = b
>>> some_list[0][0].Who()
FancyObject
Upvotes: 0
Reputation: 49856
You have a number of options:
Use the debug, gc, and introspection features to hunt down every object meeting your criterion and alter the variables while running. The disadvantage is that the value of variables will change during code execution, without it being discoverable from the affected code. Even if the change is atomic (eliminating a class of errors), because this can change the type of a variable after the execution of code which determined the value was of a different type, introduces errors which cannot reasonably be anticipated in that code. For example
a = iter(b) # will blow up if not iterable
[x for x in b] # before change, was iterable, but between two lines, b was changed to an int.
More subtly, when discriminating between string and non-string sequences (because the defining feature of strings is that iterating them also yields strings, which are themselves iterable), when flattening a structure, code may be broken.
Another answer mentions pyjack which implements option 3. Although it may work, it has all of the problems mentioned. This is likely to be appropriate only in debugging and development.
Upvotes: 1
Reputation: 386
You can put that object in global namespace of separate module and than monkey patch it when you need.
objstore.py
:
replaceable = object()
sample.py
:
import objstore
b = object()
def isB():
return objstore.replaceable is b
if __name__ == '__main__':
print isB()#False
objstore.replaceable = b
print isB()#True
P.S. Rely on monkey patching is a symptom of bad design
Upvotes: 2
Reputation: 281538
There's no way. It'd let you mutate immutable objects and cause all sorts of nastiness.
x = 1
y = (x,)
z = {x: 3}
magic_replace(x, [1])
# x is now a list!
# The contents of y have changed, and z now has an unhashable key.
x = 1 + 1
# Is x 2, or [1, 1], or something stranger?
Upvotes: 2