在另一个类中创建Python代理类

1 投票
1 回答
2343 浏览
提问于 2025-05-16 18:43

我正在尝试让一个代理类正常工作,但它似乎不太愿意合作。我想要代理的类如下:

import zmq


class ZmqPublish():
    def __init__(self):
        self.context = None
        self.pub_socket = None

    def setup(self, addr):
        self.context = zmq.Context()
        if isinstance(addr, str):
            addr = addr.split(':')
        host, port = addr if len(addr) == 2 else (addr[0], None)
        self.pub_socket = self.context.socket(zmq.PUB)
        self.pub_socket.bind('tcp://%s:%s' % (host, port))

    def send(self, msg_type, msg):
        self.pub_socket.send_multipart([msg_type, msg])

这个类的功能很简单,就是启动一个发布者,并且能够发送消息。然后我还有另一个类,负责存储数据、发送消息以及处理某种回复:

from multiprocessing import Manager
from multiprocessing.managers import BaseManager

import zmq_publish


class publishManager(BaseManager):
    pass


publishManager.register('zmq_publish.ZmqPublish', zmq_publish.ZmqPublish)


class Storage:
    def __init__(self):
        self.manager = Manager()
        self.dict = self.manager.dict()
        # Additional variables
        self.host = '127.0.0.1'
        self.port = 5555
        self.bind_addr = (self.host, self.port)
        self.publishManager = Storage.publishManager()
        self.publishManager.start()
        self.publishManager.setup(self.bind_addr)

    def setup(self):
        # Set up zmq subscriber with callbacks

    def add(self, msg):
        self.dict[msg] = 0
        self.publishManager.send('type', msg)

举个例子,add 函数被用作 zmq 进程的回调,它会添加一些信息。我的想法是,它也可以作为响应发送一条消息。问题是,这条消息从来没有发送出去,我认为这是因为回调进程和创建发布者实例的进程不是同一个。因此,我正在尝试代理这个类,并通过一个管理器让它可用,但到目前为止还没有成功。有人能帮我或者给我一些提示吗?

祝好,
帕特里克

相关问题:

  • 暂无相关问题
暂无标签

1 个回答

2

我不太明白你的问题,不过我可以给你一个提示,希望能帮你完成任务。你可以通过重载 __getattr__ 来处理访问未定义属性的情况,或者重载 __getattribute__ 来处理访问所有属性的情况。

class Proxy:
    def __init__(self, Klass): 
        self.Klass = Klass                    # Save/embed original class/object
    def __getattr__(self, name):              # On undefined attr fetches only
        ...
        return getattr(self.Klass, name)      # Delegate to Klass/ could be an obj too
    ....

这样一来,任何在 Proxy 中未定义的属性都会被 __getattr__ 拦截,并转交给 Klass。这里的 Klass 是一个类对象,所以我们实际上是在代理这个类本身,Klass 也可以是一个类的实际实例。简单来说,Proxy 只会把未定义的属性请求转交给 klass。因此,当你访问一个在 Proxy 中不存在的属性时,__getattr__ 会自动被触发,并将你的请求转交给被包装的对象 klass。如果你想拦截所有属性的请求,就需要使用 __getattribute__,而且你可能还需要使用 object.__getattribute__ 来避免在 __getattribute__ 中出现递归,因为在你的代理方法中使用任何 self.attr 都会触发你类的 __getattribute__

编辑:如何使用 Proxy 类:

如果你想代理另一个类的对象,可以这样做:

class Proxy:
    # Proxy objects of other classes 
    def __init__(self, obj): 
        self.obj = obj

    def __getattr__(self, attr): 
        return getattr(self.obj, attr)  # Run only on undefined attribute accesses 

class Test:
    data = 'some data'
    def __init__(self, x, y):
        self.x = x 
        self.y = y 
    def add(self):
        return self.x + self.y 
    def mul(self): 
        return self.x * self.y 

test_obj = Test(3, 4)        
proxied_obj = Proxy(test_obj)    # Delegate some operations to test_obj

proxied_obj.add()                # Calls Proxy.__getattr__
print(proxied_obj.mul())         # Calls Proxy.__getattr__

如你所见,addmul 方法实际上并不存在于代理其他类实例的 Proxy 类中。proxied_obj 是一个 Proxy 类的实例,里面包装了一个 Test 对象。*注意,__getattr__ 就像一个后备选项,只有在找不到属性时,__getattr__ 才会被调用。也就是说,如果在 Proxy 实例本身、它的类或父类中找不到某个属性,那么 __getattr__ 就会被触发。

你可以在 Proxy 中添加其他方法,以增强被代理对象的接口。在 Python 3.X 和新式类中,如果你的被代理对象需要重载运算符的方法,比如 __add__ 用于 +__getitem__ 用于索引,你需要在 Proxy 或其父类中重新定义所有重载方法,这样内置操作才能成功:

class Proxy:
    def __init__(self, obj): 
        self.obj = obj

    def __getattr__(self, attr): 
        return getattr(self.obj, attr)

    def   __add__  (...): ... 
    def __getitem__(...): ...

如果你还是感到困惑,可以查看这个链接:面向对象编程和委托:“包装器”代理对象

我真的鼓励你打开 Python 解释器,尝试一些小的代码实验。一行代码可能比100句废话更有效,反之亦然。

撰写回答