第8.29节 使用MethodType将Python __setattr__定义的实例方法与实例绑定

本文介绍如何使用__setattr__和MethodType动态绑定实例方法。通过具体案例展示了绑定过程及调用方式,使得动态方法如同直接定义于类内。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、 引言
《第7.14节Python类中的实例方法解析》介绍了使用“实例对象名.方法名 = MethodType(函数, 对象)”将动态定义的方法与实例进行绑定
《第8.12节 Python类中使用__dict__定义实例变量和方法》介绍了使用

self.__dict__['drive'] = MethodType(fdrive,self)

将动态定义的方法与实例进行绑定
这些绑定了方法与实例对象的方法,通过“实例.方法”调用时就无需在第一个实参中手工传递self参数,与类体中直接定义的方法调用形式及处理机制相同。而上节介绍__setattr__定义实例方法却没有提供这样的介绍,实际上通过__setattr__一样也可以实现动态定义的实例方法与实例进行绑定。

二、 绑定语法

  1. 语法:实例.__setattr__(实例方法名,MethodType(绑定函数, 实例))
  2. 其中:
    1)“实例”就是类定义的实例对象;
    2)“实例方法名”就是需要动态定义的实例方法的名字;
    3)“绑定函数”就是实例方法真正实现的在类体外定义的函数。

三、 案例

  1. 案例说明
    本节案例就是上节案例的基础上增加了一个绑定动态增加的实例方法与实例对象的处理,具体例子情况如下:
    1)从types模块中导入MethodType方法;
    2)定义了函数fdrive,准备用于实例方法定义使用;
    3)定义Car类, 其构造方法中定义了power实例变量和 totaldistance实例变量,不过totaldistance是使用__setattr__方法定义的;
    4)类定义后定义了类的实例car,在类体外使用__setattr__方法将函数绑定到car的实例方法driver,绑定时使用了MethodType方法,这样调用时就无需象上节一样传递“实例”实参。而是Python自动添加“self”。
  2. 源代码
>>> from types import MethodType #导入MethodType方法
>>> def fdrive(self,distance):self.totaldistance += distance  #定义一个函数,准备用于赋值给类的实例方法属性

>>> class Car():
    def __init__(self, power):
        self.power = power
        self.__setattr__('totaldistance',0)
        
>>> car = Car('汽油发动机')
>>> car.__setattr__('drive',MethodType(fdrive,car))  #定义实例方法drive并和实例绑定
>>> car.drive(100)  #调用新绑定的实例方法,参数中没有传入实参car
>>> car.__dict__
{'power': '汽油发动机', 'totaldistance': 100, 'drive': <bound method fdrive of <__main__.Car object at 0x0000000003805898>>}
>>>
  1. 执行截屏
    在这里插入图片描述
    上图中标记黄色部分就是与上节调用drive方法不同的地方。
    本节结合案例介绍了使用__setattr__方法定义实例方法并使用MethodType绑定该方法和实例对象的过程,其本质是与《第8.12节 Python类中使用__dict__定义实例变量和方法》的绑定是一样的。
    老猿Python,跟老猿学Python!
    博客地址:https://blog.youkuaiyun.com/LaoYuanPython

    请大家多多支持,点赞、评论和加关注!谢谢!
import inspect class KeyWordManage: _GLOBAL_ALIAS_MAP = {} # 全局别名注册表 def chinese_alias(*names: str): """ 装饰器,用于为函数添加中文别名 1. 同时支持类方法和普通函数 2. 支持多中文别名 Args: names (str): 中文别名, 可以有多个 """ # def decorator(func: T) -> T: # func.chinese_name = name # 将中文名存储为函数属性 # return func def decorator(func): # 判断方法是否有_chinese_aliases属性 if not hasattr(func, '_chinese_aliases'): # 如果方法没有_chinese_aliases属性,则创建一个空的集合 func._chinese_aliases = set() # 将中文别名添加到方法属性的集合中 func._chinese_aliases.update(names) # 普通函数注册到全局表 # import inspect # 如果方法不是类方法 if not inspect.ismethod(func): # 遍历中文别名 for name in names: # 将方法注册到全局表中 KeyWordManage._GLOBAL_ALIAS_MAP[name] = func # 返回装饰器函数 return func return decorator class ChineseAliasMeta(type): """ 自定义的元类,用于在创建类的过程中添加中文别名 1.自动完成别名注册,无需手动调用add_chinese_aliases 2.继承体系下所有子类自动获得该能力 """ def __new__(cls, name, bases, attrs): """ 创建新的类实例 Args: cls (Class): 当前类 name (str): 类名 bases (tuple): 父类列表 attrs (dict): 类属性字典, 例如:{'__init__': <function __init__ at 0x00000253747268F0>} Returns: Class: 新建的类实例 """ # 创建新的类实例, klass 是创建的类实例, 例如:<class '__main__.MyClass'> klass = super().__new__(cls, name, bases, attrs) # 遍历类的属性,查找具有chinese_name属性的函数并添加到类中 for attr_name in dir(klass): # dir(klass) 返回类的所有属性,例如:['__init__', 'add_chinese_aliases', 'chinese_alias'] # 获取属性 method = getattr(klass, attr_name) # 判断方法是否可调用且具有chinese_name属性 if callable(method) and hasattr(method, '_chinese_aliases'): for alias in method._chinese_aliases: # 将方法添加到类属性中 setattr(klass, alias, method) # # 将方法添加到类中 # setattr(klass, method.chinese_name, method) for name, func in KeyWordManage._GLOBAL_ALIAS_MAP.items(): # 如果方法不是类方法 if not inspect.ismethod(func): setattr(klass, name, func) # 返回类实例 return klass # def __call__(cls, *args, **kwargs): # import inspect # instance = super().__call__(*args, **kwargs) # for name, func in KeyWordManage._GLOBAL_ALIAS_MAP.items(): # # # 如果方法不是类方法 # if not inspect.ismethod(func): # setattr(instance, name, func) # # else: # # return instance @KeyWordManage.chinese_alias("加法1") def add1(a, b): return a + b # 使用示例2 # @add_chinese_aliases class Calculator2(metaclass=ChineseAliasMeta): @KeyWordManage.chinese_alias("加法") def add(self, a, b): return a + b + 1 class Calculator21(Calculator2): @KeyWordManage.chinese_alias("减法") def sub(self, a, b): return a - b # def __getattr__(self, name: str): # # 支持通过中文别名访问类方法 # # if name in self._alias_map: # print(f"通过中文别名访问类方法: {name}") # if name in KeyWordManage._GLOBAL_ALIAS_MAP: # return KeyWordManage._GLOBAL_ALIAS_MAP[name] # raise AttributeError(f"'{self.__name__}'没有'{name}'属性") if __name__ == '__main__': # 测试 calc = Calculator21() print(calc.加法1(2, 3)) # 调用不存在的方法,但是被关键字注册了。 print(calc.add(2, 3)) # 输出: 5 print(calc.加法(2, 3)) # 输出: 5(通过中文别名调用) print(calc.sub(2, 3)) # 输出: -1 print(calc.减法(2, 3)) # 输出: -1(通过中文别名调用) 我想实现将普通方法临时加入到类的实例中?补充完善这个功能
最新发布
06-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LaoYuanPython

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值