如下所示,在twisted.spread.flavors.RemoteCache.unjellyFor
中,我们创建了一个名为cProxy
的虚拟对象,并将其返回给客户端代码的其余部分,而不是返回self
。
def unjellyFor(self, unjellier, jellyList):
if unjellier.invoker is None:
return setInstanceState(self, unjellier, jellyList)
self.broker = unjellier.invoker
self.luid = jellyList[1]
cProxy = _newDummyLike(self)
# XXX questionable whether this was a good design idea...
init = getattr(cProxy, "__init__", None)
if init:
init()
unjellier.invoker.cacheLocally(jellyList[1], self)
cProxy.setCopyableState(unjellier.unjelly(jellyList[2]))
# Might have changed due to setCopyableState method; we'll assume that
# it's bad form to do so afterwards.
self.__dict__ = cProxy.__dict__
# chomp, chomp -- some existing code uses "self.__dict__ =", some uses
# "__dict__.update". This is here in order to handle both cases.
self.broker = unjellier.invoker
self.luid = jellyList[1]
return cProxy
_newDummyLike的主体看起来像这样:
def _newDummyLike(instance):
"""
Create a new instance like C{instance}.
The new instance has the same class and instance dictionary as the given
instance.
@return: The new instance.
"""
if isinstance(instance.__class__, type):
# New-style class
dummy = _DummyNewStyle()
else:
# Classic class
dummy = _Dummy()
dummy.__class__ = instance.__class__
dummy.__dict__ = instance.__dict__
return dummy
由于虚拟对象cProxy
与"真实"共享其__dict__
和__class__
。对象,我根本没有看到制作假人的意义。为什么要创建假人?
答案 0 :(得分:2)
这些"虚拟"的目的对象是分布式垃圾收集。
首先,让我们考虑Copyable
的简单情况。每次序列化时,您的同伴都会获得一个新的RemoteCopy
。简单 - 无需跟踪。您的Copyable
可以随时轻松收集垃圾。
接下来,Referenceable
。每次序列化时,您的同伴都会获得一个新的RemoteReference
。现在我们遇到了一个问题:如果您的同行仍然拥有RemoteReference
,那么他们应该可以在Referenceable
上调用方法,这意味着您的经纪人现在拥有对您Referenceable
的强大引用。稍微复杂一点,但仍然相当简单:每次RemoteReference
垃圾收集,RemoteReference.__del__
,我们会发送一条decref
消息,告诉发件人他们的Referenceable
不再引用。当计数变为零时,可以消除强引用,并且它将被自然地垃圾收集。这是有效的,因为RemoteReference
实际上是不可变的 - 它包含的是所讨论对象的不透明标识符,对代理的引用,以及其他内容。
最后,Cacheable
。在这里,我们遇到了一个真正的难题。您序列化Cacheable
,现在Broker
需要保持对Cacheable
的强引用,以便能够判断它是否相同Cacheable
稍后再发送。但是在电汇的另一端,RemoteCache
- 需要__del__
通知我们Cacheable
的隐含引用是否消失 - 也有来自其代理,因为Cacheable
可能需要向RemoteCache
发送更新。
这是一个循环引用,而且是一个糟糕的引用。我们需要一些方法来打破循环,以便我们交给应用程序的东西,它收集垃圾,每次服务器发送给我们RemoteCache
的不同副本时跟踪。事实证明,我们可以这样做,并打破循环引用(请记住,这段代码早于python的GC!)通过haivng一个单独的实例对象,每个对象都有自己的{{1} }方法,用于发送的每个__del__
。但是我们可以通过提供共享其内部状态(RemoteCache
)的应用程序对象来保留一致性的幻觉,而不让让__dict__
的对象通过让它们参与而无法收集一个循环。这样,当__del__
个对象被垃圾收集时,它们每个都会发送一条消息,然后我们就会离开"真实的"一个(在您的示例中为RemoteCache
)具有来自经纪人的强引用,它变为"零"引用,这也是它第一次复制的原因(你可以看到self
只有一个其他的调用站点,这是对同一个可缓存的重复引用被反序列化的结果 - 得到一个每次新的代理对象。)
希望这更有意义!
答案 1 :(得分:1)
这只是制作必要对象的一个技巧。创建一个完全任意的,用户定义类型的新实例更难。您传递给__init__
的论据是什么?如果__init__
有不良副作用怎么办?也许你可以使用它的__new__
方法 - 但这需要什么参数?或者它甚至没有__new__
方法,或者__new__
可能有副作用......等等。
与弄清楚所有这些甚至可能无法实现相比,这个技巧非常简单直接。