亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

多處理代理:讓 getters 返回代理本身

多處理代理:讓 getters 返回代理本身

德瑪西亞99 2023-12-26 17:05:23
我有一個復雜的不可選取對象,它的屬性(通過 getter 和 setter 定義)也是復雜且不可選取類型的。我想為對象創建一個多處理代理來并行執行一些任務。問題:雖然我成功地使 getter 方法可用于代理對象,但我無法使 getter 返回不可選取的返回對象的代理。我的設置類似于以下內容:from multiprocessing.managers import BaseManager, NamespaceProxyclass A():    @property    def a(self):        return B()    @property    def b(self):        return 2# unpickable classclass B():    def __init__(self, *args):        self.f = lambda: 1    class ProxyBase(NamespaceProxy):    _exposed_ = ('__getattribute__', '__setattr__', '__delattr__')class AProxy(ProxyBase): passclass BProxy(ProxyBase): passclass MyManager(BaseManager):passMyManager.register('A', A, AProxy)if __name__ == '__main__':    with MyManager() as manager:        myA = manager.A()        print(myA.b) # works great        print(myA.a) # raises error, because the object B is not pickable我知道我可以在向管理器注冊方法時指定方法的結果類型。也就是說,我可以做MyManager.register('A', A, AProxy, method_to_typeid={'__getattribute__':'B'})MyManager.register('B', B, BProxy)if __name__ == '__main__':    with MyManager() as manager:        myA = manager.A()        print(myA.a) # works great!        print(myA.b) # returns the same as myA.a ?!我很清楚,我的解決方案不起作用,因為該方法適用于所有屬性,而我只希望它在訪問屬性時__getattr__返回代理。我怎樣才能做到這一點?Ba作為一個附帶問題:如果我*args從 __init__的方法中刪除參數B,我會收到一個錯誤,表明它被調用時參數數量錯誤。為什么?我該如何解決這個問題?
查看完整描述

1 回答

?
DIEA

TA貢獻1820條經驗 獲得超2個贊

如果沒有一些技巧,這是不可能的,因為返回值或代理的選擇是僅基于方法名稱而不是返回值的類型(來自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)

另請記住,__getattribute__在調用方法時,在不可挑選的類的代理中公開基本上會破壞代理功能。


但如果你愿意破解它并且只需要屬性訪問,這里有一個可行的解決方案(注意調用myA.a.f()仍然不起作用,lambda 是一個屬性并且沒有被代理,只有方法可以,但這是一個不同的問題)。


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)


解決方案的關鍵是替換_Server我們管理器中的 ,然后將id_to_obj字典包裝為針對我們需要的特定方法執行 hack 的字典。


該黑客方法包括填充gettypeid該方法的字典,但只有在它被評估并且我們知道返回類型是我們需要代理的類型之后。幸運的是,我們的評估順序gettypeid是在調用該方法之后訪問的。


幸運的是,gettypeid它在方法中用作本地變量serve_client,因此我們可以返回它的副本并對其進行修改,并且不會引入任何并發問題。


雖然這是一個有趣的練習,但我不得不說我真的建議不要使用此解決方案,如果您正在處理無法修改的外部代碼,您應該簡單地創建自己的包裝類,該包裝類具有顯式方法而不是訪問@property器,代理您自己的類代替,并使用method_to_typeid.


查看完整回答
反對 回復 2023-12-26
  • 1 回答
  • 0 關注
  • 181 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號