1 回答

TA貢獻(xiàn)1820條經(jīng)驗(yàn) 獲得超2個(gè)贊
如果沒有一些技巧,這是不可能的,因?yàn)榉祷刂祷虼淼倪x擇是僅基于方法名稱而不是返回值的類型(來自Server.serve_client):
try:
res = function(*args, **kwds)
except Exception as e:
msg = ('#ERROR', e)
else:
typeid = gettypeid and gettypeid.get(methodname, None)
if typeid:
rident, rexposed = self.create(conn, typeid, res)
token = Token(typeid, self.address, rident)
msg = ('#PROXY', (rexposed, token))
else:
msg = ('#RETURN', res)
另請(qǐng)記住,__getattribute__在調(diào)用方法時(shí),在不可挑選的類的代理中公開基本上會(huì)破壞代理功能。
但如果你愿意破解它并且只需要屬性訪問,這里有一個(gè)可行的解決方案(注意調(diào)用myA.a.f()仍然不起作用,lambda 是一個(gè)屬性并且沒有被代理,只有方法可以,但這是一個(gè)不同的問題)。
import os
from multiprocessing.managers import BaseManager, NamespaceProxy, Server
class A():
@property
def a(self):
return B()
@property
def b(self):
return 2
# unpickable class
class B():
def __init__(self, *args):
self.f = lambda: 1
self.pid = os.getpid()
class HackedObj:
def __init__(self, obj, gettypeid):
self.obj = obj
self.gettypeid = gettypeid
def __getattribute__(self, attr):
if attr == '__getattribute__':
return object.__getattribute__(self, attr)
obj = object.__getattribute__(self, 'obj')
result = object.__getattribute__(obj, attr)
if isinstance(result, B):
gettypeid = object.__getattribute__(self, 'gettypeid')
# This tells the server that the return value of this method is
# B, for which we've registered a proxy.
gettypeid['__getattribute__'] = 'B'
return result
class HackedDict:
def __init__(self, data):
self.data = data
def __setitem__(self, key, value):
self.data[key] = value
def __getitem__(self, key):
obj, exposed, gettypeid = self.data[key]
if isinstance(obj, A):
gettypeid = gettypeid.copy() if gettypeid else {}
# Now we need getattr to update gettypeid based on the result
# luckily BaseManager queries the typeid info after the function
# has been invoked
obj = HackedObj(obj, gettypeid)
return (obj, exposed, gettypeid)
class HackedServer(Server):
def __init__(self, registry, address, authkey, serializer):
super().__init__(registry, address, authkey, serializer)
self.id_to_obj = HackedDict(self.id_to_obj)
class MyManager(BaseManager):
_Server = HackedServer
class ProxyBase(NamespaceProxy):
_exposed_ = ('__getattribute__', '__setattr__', '__delattr__')
class AProxy(ProxyBase): pass
class BProxy(ProxyBase): pass
MyManager.register('A', callable=A, proxytype=AProxy)
MyManager.register('B', callable=B, proxytype=BProxy)
if __name__ == '__main__':
print("This process: ", os.getpid())
with MyManager() as manager:
myB = manager.B()
print("Proxy process, using B directly: ", myB.pid)
myA = manager.A()
print('myA.b', myA.b)
print("Proxy process, via A: ", myA.a.pid)
解決方案的關(guān)鍵是替換_Server我們管理器中的 ,然后將id_to_obj字典包裝為針對(duì)我們需要的特定方法執(zhí)行 hack 的字典。
該黑客方法包括填充gettypeid該方法的字典,但只有在它被評(píng)估并且我們知道返回類型是我們需要代理的類型之后。幸運(yùn)的是,我們的評(píng)估順序gettypeid是在調(diào)用該方法之后訪問的。
幸運(yùn)的是,gettypeid它在方法中用作本地變量serve_client,因此我們可以返回它的副本并對(duì)其進(jìn)行修改,并且不會(huì)引入任何并發(fā)問題。
雖然這是一個(gè)有趣的練習(xí),但我不得不說我真的建議不要使用此解決方案,如果您正在處理無法修改的外部代碼,您應(yīng)該簡(jiǎn)單地創(chuàng)建自己的包裝類,該包裝類具有顯式方法而不是訪問@property器,代理您自己的類代替,并使用method_to_typeid.
添加回答
舉報(bào)