python设计模式(Adapter)

本文最后更新于 2023年8月8日 早上

最近在整合各个语言模型,就涉及功能相同,但是命名千差万别的情况,于是用到了适配器模式,顺便产出本文简单说说该模式。

说明

适配器模式(Adapter Pattern)是一种结构型设计模式,它的主要目标是使得原本不兼容的接口能够相互工作。这种模式通常用于系统需要使用现有的类,而这些类的接口不符合系统的需求的情况。

在Python中,适配器模式可以用来解决两个接口不兼容的问题。例如,你可能有一个旧的代码库,或者一个来自第三方库的类,它的接口和你的代码不兼容。在这种情况下,你可以创建一个适配器,它包装了这个旧的类,并提供一个新的接口,使得旧的类可以被你的代码使用。

示例

假设chatgpt模型类如下,和模型相关的初始化由函数init实现,对话由函数request。就是随便写个例子,能够展示适配器效果就行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class chatgpt:

    def __init__(self) -> None:

        self.name = "chatgpt"

        print("chatgpt init")


    def init(self):

        print("chatgpt load")


    def request(self) -> str:

        return "chatgpt request"

然后另一个chatglm2的类,的模型相关初始化名字是awaken,对话函数是ack:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class chatglm2:

    def __init__(self) -> None:

        self.name = "chatglm2"

        print("chatglm2 init")

    def awaken(self):

        print("chatglm2 load")

    def ack(self) -> str:

        return "chatglm2 request"

假如已经写好了通用接口,定义模型初始化函数是load,对话函数是chat,上面的两个类中同样功能的函数名字各异,那么就使用适配器给搞成一样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from typing import Callable, TypeVar
T = TypeVar("T")

class Adapter:
    def __init__(self, obj: T, **adapted_methods: Callable):
        self.obj = obj
        # 将原本的方法以新的名字添加到适配器实例的字典中
        self.__dict__.update(adapted_methods)
        print(self.__dict__)

    # 用于处理对适配器实例的属性访问。当适配器实例无法找到某个属性时,它会委托给 self.obj 对象,并返回相应属性的值
    # 具体来说,当我们通过适配器实例访问一个属性时,如果适配器实例本身没有这个属性,那么 __getattr__ 方法会被调用。在这个方法中,getattr(self.obj, attr) 表达式会尝试从 self.obj 对象中获取相应的属性。
    def __getattr__(self, attr):
        return getattr(self.obj, attr)

    #  方法返回被适配对象的 __dict__ 属性,即原始对象的属性字典。
    def original_dict(self):
        return self.obj.__dict__
       
adaptee1 = chatgpt()
adapter1 = Adapter(adaptee1, load=adaptee1.init, chat=adaptee1.request)

adapter1.load()
print(adapter1.chat())

adaptee2 = chatglm2()
adapter2 = Adapter(adaptee2, load=adaptee2.awaken, chat=adaptee2.ack)

adapter2.load()
print(adapter2.chat())

这样就可以在后面使用的时候均统一函数名字,而不需要根据不同类去写判断。

上列代码输出:

1
2
3
4
5
6
7
8
chatgpt init
{'obj': <__main__.chatgpt object at 0x0000018DE98ABFD0>, 'load': <bound method chatgpt.init of <__main__.chatgpt object at 0x0000018DE98ABFD0>>, 'chat': <bound method chatgpt.request of <__main__.chatgpt object at 0x0000018DE98ABFD0>>}
chatgpt load
chatgpt request
chatglm2 init
{'obj': <__main__.chatglm2 object at 0x0000018DE98ABA30>, 'load': <bound method chatglm2.awaken of <__main__.chatglm2 object at 0x0000018DE98ABA30>>, 'chat': <bound method chatglm2.ack of <__main__.chatglm2 object at 0x0000018DE98ABA30>>}
chatglm2 load
chatglm2 request

python设计模式(Adapter)
https://blog.kala.love/posts/3fbe32b8/
作者
久远·卡拉
发布于
2023年8月8日
许可协议