python - 在 Python 3 中创建自动委托(delegate)包装类

标签 python python-3.x metaprogramming wrapper delegation

我在 Python 3(.8) 中创建了一个委托(delegate)包装类,它允许我完全复制基类的 API、委托(delegate)给底层实例,并在必要时交换底层实例.

我仍然没有获得完整的 API 同等性,所以我想我应该分享并寻求帮助。

我知道这是一个奇怪的选择,但我需要支持现有的 API,该 API (a) 非常广泛,并且 (b) 不在我的控制范围内(第 3 方库),同时能够交换底层实例调用者代码不知道。例如:

with MyResource() as r:  # MyResource is my class, while an `r` instance is an instance of a class in a 3rd party library.
  # r should be wrapped in the delegating instance
  r.do_x()
  # -- under the hood, call .__exit__ on the base instance of `r`, and regenerate it without the calling code knowing.
  r.do_y()  # this is already delegated to the new instance of base `r`

这是我迄今为止的代码:

T = TypeVar("T")

class SwappableWrapper(Generic[T]):
    def __init__(self, base: T):
        self.__base: T = base
        for name in [_ for _ in dir(base) if _ not in ["__class__", "__dict__", "__weakref__"]]:
            class_attr = SwappableWrapper.__get_class_attr(type(base), name)
            if class_attr:
                is_property = isinstance(class_attr, property)
                is_function = not is_property and callable(getattr(base, name))
                if is_function:
                    setattr(self, name, self.__call_base_instance(name))
                elif is_property:
                    self.__set_property(name)
                else:  # make even the normal attributes into getter-style delegating properties.
                    self.__set_property(name)

    @staticmethod
    def __get_class_attr(clazz, attr_name: str):
        if hasattr(clazz, attr_name):
            return getattr(clazz, attr_name)
        else:
            for parent in clazz.__bases__:
                attr = SwappableWrapper.__get_class_attr(parent, attr_name)
                if attr:
                    return attr
        return None

    def __call_base_instance(self, func_name):
        def do_call(*args, **kwargs):
            return getattr(self.__get_base_instance(), func_name)(*args, **kwargs)
        return do_call

    def __set_property(self, name):
        setattr(self, name, property(fget=lambda: getattr(self.__get_base_instance(), name),
                                     fset=lambda v: setattr(self.__get_base_instance(), name, v),
                                     fdel=lambda: delattr(self.__get_base_instance(), name)))

    def __get_base_instance(self) -> T:
        return self.__base

    def swap_base_instance_(self, new_base: T) -> None:
        print(f"Swap old base: {self.__base} New base: {new_base}")
        self.__base = new_base

调用代码的打印方式很奇怪 - 它没有调用新创建的委托(delegate)属性,而是只打印 property(...)

很乐意提供任何帮助。谢谢!

最佳答案

我刚刚意识到使用 Python 的动态调度可以轻松解决这个问题。对于后代,您可以通过以下方式实现这一目标:

class SwappableWrapper(Generic[T]):
    def __init__(self, base: T):
        self.__base: T = base

    def __getattr__(self, item):
        return getattr(self.__base, item)

    def swap_base_instance_(self, new_base: T) -> None:
        self.__base = new_base

如需更深入的解释,see this SO question .

关于python - 在 Python 3 中创建自动委托(delegate)包装类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72195089/

相关文章:

python - 将html实体替换为Python 2.6中对应的utf-8字符

python - 您可以更改 Tornado 应用程序的日志输出格式吗?

导致退格键击的Python代码?

c++ - 使用模板和/或 constexpr 在编译时构建函数

python - 使用 setuptool 从构建的 rpm 发行版中排除源文件

python - 如何在另一个 ndarray 中找到一个 ndarray 的索引

python - 无法从给定的 python 代码生成 S=>Ba

python - 将多个列表写入 CSV 中单个单元格中的不同列

c++ - 元编程是否用于现实世界的 C++ 软件项目?

c++ - 如何在模板元编程中进行小于比较?