Automatically readdress all variables referring to an object
Suppose I have in python this object
class Foo:
def __init__(self, val):
self.val = val
and these two variables
a=Foo(5)
b=a
both b
and a
refer to the same instance of Foo()
, so any modification to the attribute .val
will be seen equally and synchronized as a.val
and b.val
.
>>> b.val
5
>>> b.val=3
>>> a.val
3
Now suppose I want to say a=Foo(7)
. This will create another instance of Foo, so now a
and b
are independent.
My question is: is there a way to have b
readdressed automatically to the new Foo() instance, without using an intermediate proxy object? It's clearly not possible with the method I present开发者_运维问答ed, but maybe there's some magic I am not aware of.
As Aaron points out, there may be very hacky and fragile solutions but there is likely nothing that would be guaranteed to work across all Python implementations (e.g. CPython, IronPython, Jython, PyPy, etc). But why would one realistically want to do something that is so contrary to the design and idiomatic use of the language when there are simple idiomatic solutions available? Calling a class object typically returns an instance object. Names are bound to that object. Rather than trying to fight the language by coming up with hacks to track down references to objects in order to update bindings, the natural thing would be to design a mutable instance object, if necessary using a simple wrapper around an existing class.
Updated for aaronasterling:
for d in gc.get_referrers(old):
if isinstance(d,list):
new_list = [ new if item is old else item for item in d]
while d: del d[-1]
d.extend(new_list)
elif isinstance(d,dict):
try:
for item in d:
if d[item] is old: d[item] = new
except Exception:pass
else: print('cant handle referrer at %i' % id(d))
Also you dont need to readdress reference to make it's instance equal to some other object. You can just write
new.__dict__ = old.__dict__
new.__class__ = old.__class__
But this will work only with not-built-in class instances.
update:
I figured out how to make it work with tuples. This is basically Odomontois' solution with some type checking removed and made recursive for tuples.
import gc
import inspect
def update(obj, value):
objects = gc.get_referrers(obj)
old = obj # this protects our reference to the initial object
for o in objects:
if hasattr(o, '__iter__') and hasattr(o, 'extend'): # list like objects
new_list = [value if item is old else item for item in o]
while o: del o[-1]
o.extend(new_list)
elif hasattr(o, '__iter__') and hasattr(o, 'keys'): # dictionary like objects
for item in o.keys():
if o[item] is old: o[item] = value
elif isinstance(o, set):
o.remove(old)
o.add(value)
elif isinstance(o, tuple):
new_tuple = tuple(value if item is old else item for item in o)
update(o, new_tuple)
elif inspect.isframe(o):
continue
else:
raise Exception("can't handle referrer {0}".format(o))
class Test(object):
def __init__(self, obj):
self.val = obj
a = (1, ) #works
b = a #works
t = Test(b) #works because we this t.__dict__
L = [a] # works
S = set((a, )) # works
T = (a, ) # works
update(a, (2, ))
print a, b, t.val, L, S, T
wrong answer
deleted
Maybe and just maybe you're looking for a Singleton
class Singleton(type):
def __init__(cls, name, bases, dict):
super(Singleton, cls).__init__(name, bases, dict)
cls.instance = None
def __call__(cls, *args, **kw):
if cls.instance is None:
cls.instance = super(Singleton, cls).__call__(*args, **kw)
return cls.instance
class MyClass(object):
__metaclass__ = Singleton
print MyClass()
print MyClass()
精彩评论