访问类的多处理代理的属性

时间:2014-10-22 02:47:48

标签: python multiprocessing

我有一个类,我想以只读方式与池中的子进程共享,所以我准备了一个类的代理,但它没有用。以下是我的问题的简化示例。

from multiprocessing.managers import BaseManager

class TestClass:
    def __init__(self, a):
        self.a = a
    def b(self):
        print self.a

class MyManager(BaseManager): pass

MyManager.register('test', TestClass)

if __name__ == '__main__':
    manager = MyManager()
    manager.start()
    t = TestClass(1)
    print t.a
    mt = manager.test(2)
    mt.b()
    mt.a

当我运行此代码时,我得到:

1
2
Traceback (most recent call last):
  File "multiprocess_example_stackexchange.py", line 20, in <module>
    mt.a 
AttributeError: 'AutoProxy[test]' object has no attribute 'a'

似乎我无法通过代理直接访问共享对象的属性。是使用获取属性的方法的唯一方法,还是我做错了什么?

6 个答案:

答案 0 :(得分:14)

Proxy及其子类使用的multiprocessing.BaseManager对象通常只从他们所引用的对象中公开方法,而不是属性。现在,有multiprocessing.Manager().Namespace,它提供了一个Proxy子类, 提供对属性的访问,而不是方法。我们可以创建自己的Proxy类型,它继承了该类型,可以访问我们的所有属性,以及访问b函数:

from multiprocessing.managers import BaseManager, NamespaceProxy

class TestClass(object):
    def __init__(self, a):
        self.a = a

    def b(self):
        print self.a

class MyManager(BaseManager): pass

class TestProxy(NamespaceProxy):
    # We need to expose the same __dunder__ methods as NamespaceProxy,
    # in addition to the b method.
    _exposed_ = ('__getattribute__', '__setattr__', '__delattr__', 'b')

    def b(self):
        callmethod = object.__getattribute__(self, '_callmethod')
        return callmethod('b')

MyManager.register('test', TestClass, TestProxy)

if __name__ == '__main__':
    manager = MyManager()
    manager.start()
    t = TestClass(1)
    print t.a
    mt = manager.test(2)
    print mt.a
    mt.a = 5
    mt.b()

输出:

1
2
5

修改

如果您希望能够将原始类中的方法动态添加到Proxy类,可以执行以下操作:

from multiprocessing.managers import BaseManager, NamespaceProxy
import inspect

class TestClass(object):
    def __init__(self, a):
        self.a = a

    def b(self):
        print self.a

class AnotherClass(object):
    def __init__(self, a):
        self.a = a

    def c(self):
        print self.a

class MyManager(BaseManager): pass

class ProxyBase(NamespaceProxy):
    _exposed_ = ('__getattribute__', '__setattr__', '__delattr__')

class TestProxy(ProxyBase): pass
class AnotherProxy(ProxyBase): pass


def register_proxy(name, cls, proxy):
    for attr in dir(cls):
        if inspect.ismethod(getattr(cls, attr)) and not attr.startswith("__"):
            proxy._exposed_ += (attr,)
            setattr(proxy, attr, 
                    lambda s: object.__getattribute__(s, '_callmethod')(attr))
    MyManager.register(name, cls, proxy)

register_proxy('test', TestClass, TestProxy)
register_proxy('another', AnotherClass, AnotherProxy)

if __name__ == '__main__':
    manager = MyManager()
    manager.start()
    mt = manager.test(2)
    ma = manager.another(3)
    mt.b()
    ma.c()
    mt.a = 5
    ma.a = 6
    mt.b()
    ma.c()

答案 1 :(得分:1)

这是传递参数(例如:__getitem__)或不传递参数(例如:__len__)的示例:

class TestProxy(NamespaceProxy):
    _exposed_ = ('__getattribute__', '__setattr__', '__delattr__','__len__','__getitem__')

    def __len__(self):
        callmethod = object.__getattribute__(self, '_callmethod')
        return callmethod('__len__')
    def __getitem__(self,index):
        callmethod = object.__getattribute__(self, '_callmethod')
        return callmethod('__getitem__',(index,))

答案 2 :(得分:1)

花了几个小时阅读源代码之后,这是实现代理类以公开所有属性和方法的最简单方法:

class TestProxy(NamespaceProxy):
    _exposed_ = tuple(dir(Test))

    def __getattr__(self, name):
        result = super().__getattr__(name)
        if isinstance(result, types.MethodType):
            def wrapper(*args, **kwargs):
                self._callmethod(name, args)
            return wrapper
        return result

BaseManager.register('Test', Test, TestProxy)

manager = BaseManager()
test = manager.Test()

此外,这是一种自动代理方法:

def Proxy(target):
    dic = {'types': types}
    exec('''def __getattr__(self, key):
        result = self._callmethod('__getattribute__', (key,))
        if isinstance(result, types.MethodType):
            def wrapper(*args, **kwargs):
                self._callmethod(key, args)
            return wrapper
        return result''', dic)
    proxyName = target.__name__ + "Proxy"
    ProxyType = type(proxyName, (NamespaceProxy,), dic)
    ProxyType._exposed_ = tuple(dir(target))
    return ProxyType

TestProxy = Proxy(Test)
BaseManager.register('Test', Test, TestProxy)

manager = BaseManager()
test = manager.Test()

答案 3 :(得分:1)

由于我没有足够的声誉来发表评论,所以我发布了一个答案。 @shtse8 的其他优秀答案有一个错误。我想指出这一点,因为当您搜索有关 NamespaceProxy 的查询时,此页面是最热门的页面之一,并且上述答案已被其他人用作 well

问题在于以下代码:

class TestProxy(NamespaceProxy):
    _exposed_ = tuple(dir(Test))

    def __getattr__(self, name):
        result = super().__getattr__(name)
        if isinstance(result, types.MethodType):
            def wrapper(*args, **kwargs):
                self._callmethod(name, args)  # Result not returned
            return wrapper
        return result

如果您使用此类(或答案中提到的等效“自动代理”方法)来创建代理对象,则所有编程为返回值的函数将始终返回 NoneType(如果您从代理)。这是因为在包装器中没有返回方法调用的结果。因此,我们需要在 self._callmethod(name, args) 行中放置一个 return。

TestProxy 类应该变成:

class TestProxy(NamespaceProxy):
    _exposed_ = tuple(dir(Test))

    def __getattr__(self, name):
        result = super().__getattr__(name)
        if isinstance(result, types.MethodType):
            def wrapper(*args, **kwargs):
                return self._callmethod(name, args)  # Note the return here
            return wrapper
        return result

“自动代理”功能将变为:

def Proxy(target):
    dic = {'types': types}
    exec('''def __getattr__(self, key):
        result = self._callmethod('__getattribute__', (key,))
        if isinstance(result, types.MethodType):
            def wrapper(*args, **kwargs):
                return self._callmethod(key, args)
            return wrapper
        return result''', dic)
    proxyName = target.__name__ + "Proxy"
    ProxyType = type(proxyName, (NamespaceProxy,), dic)
    ProxyType._exposed_ = tuple(dir(target))
    return ProxyType

答案 4 :(得分:0)

这是一个不那么冗长的选择,我发现它在实践中运作良好。不确定是否有任何缺点。

class TestClass:
    def __init__(self, a):
        self.a = a
    def b(self):
        print self.a

def wrap_test_class(*args, **kwargs):
    obj = TestClass(*args, **kwargs)
    obj.get_a = lambda: obj.a
    return obj

class MyManager(BaseManager): pass

MyManager.register('test', wrap_test_class)

这样,您就可以通过调用a

来访问proxy_object.get_a()

答案 5 :(得分:0)

Charcit's solution 为我工作,只是我做了一个小的完成/错误修复。不能将 kwargs 传递给被调用的方法。所以固定版本:

class TestProxy(NamespaceProxy):
    _exposed_ = tuple(dir(Test))

    def __getattr__(self, name):
        result = super().__getattr__(name)
        if isinstance(result, types.MethodType):
            def wrapper(*args, **kwargs):
                return self._callmethod(name, args, kwargs)  # args and kwargs!
            return wrapper
        return result

未测试“autoproxy”方法,但此修复程序也应适用于此处:

def Proxy(target):
    dic = {'types': types}
    exec('''def __getattr__(self, key):
        result = self._callmethod('__getattribute__', (key,))
        if isinstance(result, types.MethodType):
            def wrapper(*args, **kwargs):
                return self._callmethod(key, args, kwargs)
            return wrapper
        return result''', dic)
    proxyName = target.__name__ + "Proxy"
    ProxyType = type(proxyName, (NamespaceProxy,), dic)
    ProxyType._exposed_ = tuple(dir(target))
    return ProxyType