透明代理(已弃用)

警告

这是一个很久以前尝试过的实验性功能,我们没有找到真正好的用例。基本功能仍然存在,但我们不建议使用它。以下示例中的一些可能不再有效(例如,您不能再对列表对象进行 tproxy)。其余部分可以通过在标准 Python 中进行黑客攻击来完成。如果有人有兴趣再次研究 tproxy,欢迎他们,但我们不认为这是一个有趣的扩展。

PyPy 的透明代理允许将对象上的操作路由到可调用对象。应用程序级代码可以自定义对象而不干扰类型系统 - type(proxied_list) is listproxied_list 是代理的内置 list 时保持为真 - 同时让你完全控制对 proxied_list 执行的所有操作。

有关透明代理的更多背景、动机和用法,请参见 [D12.1]

核心机制示例

以下示例代理了一个列表,并在任何加法操作上返回 42

$ py.py --objspace-std-withtproxy
>>>> from __pypy__ import tproxy
>>>> def f(operation, *args, **kwargs):
>>>>    if operation == '__add__':
>>>>         return 42
>>>>    raise AttributeError
>>>>
>>>> i = tproxy(list, f)
>>>> type(i)
list
>>>> i + 3
42

记录内置对象上所有操作的示例

假设我们想要一个列表,它存储对它执行的所有操作以供以后分析。我们可以使用 lib_pypy/tputil.py 模块来帮助透明地代理内置实例

from tputil import make_proxy

history = []
def recorder(operation):
    history.append(operation)
    return operation.delegate()

>>>> l = make_proxy(recorder, obj=[])
>>>> type(l)
list
>>>> l.append(3)
>>>> len(l)
1
>>>> len(history)
2

make_proxy(recorder, obj=[]) 创建一个透明列表代理,它允许我们将操作委托给 recorder() 函数。调用 type(l) 根本不会导致任何操作执行。

请注意,append() 显示为 __getattribute__(),而 type(l) 根本没有显示 - 类型是代理控制器无法更改的实例的唯一方面。

透明代理 PyPy 内置对象和支持

如果您正在使用 –objspace-std-withtproxy 选项,则 __pypy__ 模块提供以下内置函数

tproxy(type, controller)

返回一个代理对象,该对象表示给定的类型,并将所有对该类型的操作转发到控制器。在每次操作时,都会调用 controller(opname, *args, **kwargs)

get_tproxy_controller(obj)

返回给定对象的负责控制器。对于非代理对象,将返回 None

tputil 辅助模块

lib_pypy/tputil.py 模块提供

make_proxy(controller, type, obj)

创建一个由给定 controller 可调用对象控制的透明代理。该代理将显示为给定类型的完全常规实例,但对其的所有操作都将发送到指定的控制器 - 控制器在每次操作时都会收到一个 ProxyOperation 实例。如果 type 未指定,则默认为 type(obj)(如果指定了 obj)。

ProxyOperation 实例具有以下属性

proxyobj

此操作的透明代理对象。

opname

此操作的名称。

args

此操作的任何位置参数。

kwargs

此操作的任何关键字参数。

obj

(仅当提供给 make_proxy() 时)

一个具体对象。

delegate()

如果在调用 make_proxy() 时指定了具体对象实例 obj,则可以调用 proxyoperation.delegate() 将操作委托给对象实例。

其他要点

可以使用透明代理执行许多任务,包括但不限于

  • 对象的远程版本,我们可以在其上直接执行操作(想想透明分布)。
  • 访问持久性存储,例如数据库(想象一个看起来像任何其他对象的 SQL 对象映射器)。
  • 访问外部数据结构,例如其他语言,作为普通对象(当然,某些操作可能会引发异常,但由于操作是在应用程序级别执行的,因此这不是主要问题)。

实现说明

PyPy 的标准对象空间允许我们在内部拥有类型的多个实现,并在运行时更改实现,而应用程序级代码始终看到完全相同的类型和对象。已经实现了使用这些功能的多个性能优化:替代对象实现。透明代理使用此架构将控制权返回到应用程序级代码。

透明代理是在 标准对象空间 之上实现的,位于 pypy/objspace/std/proxyobject.pypypy/objspace/std/proxyobject.pypypy/objspace/std/transparent.py 中。要使用它们,您需要将 –objspace-std-withtproxy 选项传递给 pypytranslate.py。这将注册名为 W_TransparentXxx 的实现 - 通常对应于适当的 W_XxxObject - 并且包括一些解释器技巧,用于那些过于接近解释器而无法在标准对象空间中实现的对象。可以通过这种方式代理的对象类型包括用户创建的类和函数、列表、字典、异常、回溯和帧。

[D12.1]高级后端和解释器特性原型,PyPy EU 报告,2007 年,https://foss.heptapod.net/pypy/extradoc/-/tree/branch/extradoc/eu-report/D12.1_H-L-Backends_and_Feature_Prototypes-2007-03-22.pdf