有没有支持对象消息传递的Python包?

Is there a Python package that supports object message passing?

我有兴趣使我当前的 Python 项目符合面向对象的范式,因为它最初是由 Alan Kay 构想的——from my understanding,它是关于对象消息传递的——其他与对象本身有关。

我正在考虑的那种包中的示例代码可能如下所示:

def do_laundry(self):
    send(self.car, "DRIVE_TO", (self.laundromat))
    washing_machine = self.look_for(WashingMachine)
    send(self.washing_machine, "LOAD")
    ...

我可以看到如何严格遵守这种范式可以简化对象接口,消息传递的基础知识似乎并不难实现,所以我很惊讶我找不到任何东西相关的。 (我发现的所有与消息相关的包都与电子邮件或其他 Internet 活动有关。)是否有一个包可以满足我的需求,或者我是否错误地认为这对 Python 拥有?可以想象这还没有完成并且可能有用,但我过去的经验表明这不太可能。

P.S。我知道 this question,但它有不同的重点,它已经 10 年没有活动了,link 到它所指的 Python-特定资源是坏了。

您可以使用发布子系统来完成此类工作。我是像 rxpy 这样的工具的忠实粉丝,但是 Pub-Sub 的核心概念很简单,您自己可以添加。以此为例:

class DriveTo:
    def drive_to(self, location):
        print(f"Driving To {location}")


class LoadWashingMachine:
    def load_washing_machine(self):
        print(f"Loading washing machine")


class DoLaundry:
    def __init__(self, laundromat):
        self._laundromat = laundromat
        self._drive_to_events = []
        self._load_washing_machine_events = []

    def _drive_to(self, location):
        for drive_event in self._drive_to_events:
            drive_event.drive_to(location)

    def _load_washing_machine(self):
        for washing_machine_event in self._load_washing_machine_events:
            washing_machine_event.load_washing_machine()

    def do_laundry(self):
        self._drive_to(self._laundromat)
        self._load_washing_machine()

在这里我们看到每个组件都可以连接起来,而不需要了解它们的消息接收者。在您的应用程序的主体中,您将处理将每个对象链接到适当的事件方法。将其放在一起的示例:

drive_to = DriveTo()
load_washing_machine= LoadWashingMachine()
do_laundry = DoLaundry(laundromat="cleaners")

do_laundry.drive_to_events.append(drive_to)
do_laundry.load_washing_machine_events.append(load_washing_machine)

do_laundry.do_laundry()

更进一步,我们可以看到每个事件的逻辑都是冗余的。我们可以通过为事件创建一个通用的 class 来简化它。每种类型的消息都可以是不同的可观察实例,接收者可以指定他们订阅的事件类型。

class Observer(ABC):
    @abstractmethod
    def on_next(self, *args, **kwargs):
        pass


class Observable:
    def __init__(self):
        self._observers = []

    def subscribe(self, observer):
        self._observers.append(observer)

    def on_next(self, *args, **kwargs) -> None:
        for observer in self._observers:
            observer.on_next(*args, **kwargs)

重构我们的初始示例:

class DriveTo(Observer):
    def on_next(self, location):
        print(f"Driving To {location}")


class LoadWashingMachine(Observer):
    def on_next(self):
        print(f"Loading washing machine")


class DoLaundry:
    def __init__(self, laundromat):
        self._laundromat = laundromat
        self.drive_observable = Observable()
        self.load_observable = Observable()

    def do_laundry(self):
        self.drive_observable.on_next(self._laundromat)
        self.load_observable.on_next()


do_laundry = DoLaundry(laundromat="cleaners")
do_laundry.drive_observable.subscribe(DriveTo())
do_laundry.load_observable.subscribe(LoadWashingMachine())

do_laundry.do_laundry()

rxpy 库提供了很多这样的实用程序,可以让您非常轻松地编写代码。它可以处理过滤流、组合它们、处理异常、取消订阅事件等事情。

另一种使对象更易于组合的选择是使用依赖注入 (DI) 框架或控制反转 (IoC) 框架,例如 pinject.

在这些系统中,我们可以将依赖项定义为普通构造函数参数,但允许 IoC 系统处理将它们连接在一起。开始让你 类 像往常一样:

class Car:
    def drive_to(self, location):
        print(f"Driving To {location}")


class WashingMachine:
    def load(self):
        print(f"Loading washing machine")


class DoLaundry:
    def __init__(self, laundromat, car, washing_machine):
        self._laundromat = laundromat
        self._car = car
        self._washing_machine = washing_machine

    def do_laundry(self):
        self._car.drive_to(self._laundromat)
        self._washing_machine.load()

现在我们可以让 pinject 将所有东西挂钩在一起。对于 consts 之类的东西,您可以使用绑定规范来帮助 pinject 确定值应该是什么:

import pinject


class BindingSpec(pinject.BindingSpec):
    def provide_laundromat(self):
        return "cleaners"


obj_graph = pinject.new_object_graph(binding_specs=[BindingSpec()])
do_laundry = obj_graph.provide(DoLaundry)
do_laundry.do_laundry()