自动重新读取引用对象的所有变量

时间:2010-08-15 21:37:24

标签: python

假设我在python中有这个对象

class Foo:  
    def __init__(self, val):
        self.val = val

和这两个变量

a=Foo(5)
b=a

ba都引用Foo()的同一个实例,因此对.val属性的任何修改都会被视为同等且同步为a.valb.val

>>> b.val
5
>>> b.val=3
>>> a.val
3

现在假设我想说a=Foo(7)。这将创建另一个Foo实例,因此现在ab是独立的。

我的问题是:有没有办法让b自动重新读取到新的Foo()实例,而不使用中间代理对象?用我提出的方法显然不可能,但也许有一些我不知道的魔法。

4 个答案:

答案 0 :(得分:7)

正如Aaron指出的那样,可能存在非常简陋且易碎的解决方案,但可能无法保证所有Python实现(例如CPython,IronPython,Jython,PyPy等)都能正常工作。但是,当有简单的惯用解决方案时,为什么人们会想要做一些与语言的设计和习惯用法相悖的事情?调用类对象通常会返回一个实例对象。名称绑定到该对象。通过提出黑客来追踪对象的引用以更新绑定,而不是试图通过黑客来对抗语言,自然要做的是设计一个可变的实例对象,必要时使用现有类的简单包装器。

答案 1 :(得分:2)

更新了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))

此外,您不需要readdress引用使其实例等于某个其他对象。 你可以写

new.__dict__ = old.__dict__
new.__class__ = old.__class__

但这只适用于非内置类实例。

答案 2 :(得分:1)

更新

我想出了如何让它与元组一起工作。这基本上是Odomontois的解决方案,删除了一些类型检查,并为元组递归。

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

错误答案

删除

答案 3 :(得分:0)

也许,也许你正在寻找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()