Python: 如何传递一个 Autoproxy 对象

Python: How to pass an Autoproxy object

我需要将一个对象的代理传递给另一个对象,但每当我这样做时,所有其他对象得到的都是代理的引用,而不是代理本身。这与我正在尝试做的类似:

import multiprocessing.managers as m

class Foo(object):

    def __init__(self):
        self.manager = MyManager()
        self.manager.register('Bar', Bar)
        self.manager.start()
        self.bar = self.manager.Bar()
        self.bar.set_proxy(self.bar)


class Bar(object):

    def __init__(self):
        self.proxy = None

    def set_proxy(self, proxy):
        self.proxy = proxy

class MyManager(m.BaseManager):
    pass

test = Foo()

每当我这样做时,self.proxy 中的值是我创建的 Foo 实例,而不是管理器返回的代理。

这是因为设计缺陷,或者可能是 a bugProxy 实例被解封的方式。现在,如果 unpickling 代码看到你在 Manager 中 运行,它会在你 unpickle 时给你指示对象,而不是 Proxy:

def RebuildProxy(func, token, serializer, kwds):
    '''
    Function used for unpickling proxy objects.

    If possible the shared object is returned, or otherwise a proxy for it.
    '''
    server = getattr(process.current_process(), '_manager_server', None)

    if server and server.address == token.address:
        return server.id_to_obj[token.id][0] # This returns the referent
    else:
        incref = (
            kwds.pop('incref', True) and
            not getattr(process.current_process(), '_inheriting', False)
            )
        return func(token, serializer, incref=incref, **kwds) # This returns the Proxy

有时这可能是可取的,但有时不是,就像您的情况一样。您可以通过替换在您的程序中执行 unpickling 的 ReduceProxy 函数来解决它:

import multiprocessing.managers as m
from multiprocessing import process

def RebuildProxyNoReferent(func, token, serializer, kwds):
    '''
    Function used for unpickling proxy objects.

    The Proxy object is always returned.
    '''
    incref = (
    kwds.pop('incref', True) and
    not getattr(process.current_process(), '_inheriting', False)
    )
    return func(token, serializer, incref=incref, **kwds)

m.RebuildProxy = RebuildProxyNoReferent  # Monkey-patch it

class Foo(object):

    def __init__(self):
        self.manager = MyManager()
        self.manager.register('Bar', Bar)
        self.manager.start()
        self.bar = self.manager.Bar()
        print(type(self.bar))
        self.bar.set_proxy(self.bar)


class Bar(object):

    def __init__(self):
        self.proxy = None

    def set_proxy(self, proxy):
        print("got {}".format(type(proxy)))
        self.proxy = proxy

class MyManager(m.BaseManager):
    pass

test = Foo()

如果你不想猴子补丁,你也可以子类化 BaseProxy,虽然它需要更多的工作:

import multiprocessing.managers as m
from multiprocessing.managers import BaseProxy
from multiprocessing import process

def RebuildProxyNoReferent(func, token, serializer, kwds):
    '''
    Function used for unpickling proxy objects.

    If possible the shared object is returned, or otherwise a proxy for it.
    '''
    incref = (
    kwds.pop('incref', True) and
    not getattr(process.current_process(), '_inheriting', False)
    )
    return func(token, serializer, incref=incref, **kwds)

class MyProxy(BaseProxy):
    _exposed_ = ("set_proxy",)
    def set_proxy(self, arg):
        self._callmethod('set_proxy', (arg,))

    def __reduce__(self):
        ret = super(MyProxy, self).__reduce__()
        # RebuildProxy is the first item in the ret tuple.
        # So lets replace it, just for our proxy.
        ret = (RebuildProxyNoReferent,) + ret[1:]
        return ret

class Foo(object):

    def __init__(self):
        self.manager = MyManager()
        self.manager.register('Bar', Bar, MyProxy)
        self.manager.start()
        self.bar = self.manager.Bar()
        print(type(self.bar))
        self.bar.set_proxy(self.bar)