zoukankan      html  css  js  c++  java
  • 第8.30节 重写Python __setattr__方法实现属性修改捕获

    一、 引言
    《第8.26节 重写Python类中的__getattribute__方法实现实例属性访问捕获》章节介绍了__getattribute__方法,可以通过重写该方法,截获所有通过“实例名.属性名”访问实例变量、类变量、实例方法的所有操作,这是一个非常符合Python风格的方法。类似的,Python提供了__setattr__方法截获所有给属性赋值的能操作,在本节的前2节介绍了调用__setattr__方法进行属性设置的方法,本节将介绍重写__setattr__方法并以此验证所有属性值的修改Python都会隐式地调用__setattr__方法。

    二、 重写__setattr__方法
    虽然可以动态重写__setattr__方法,但老猿并不建议这样做,我们还是建议在类中直接通过定义重写__setattr__()方法你,这样最高效也最符合编程者的思维。

    1. 语法:setattr(self, name, value)
    2. 语法释义:
      1)self,就是对象自身,如果有不理解的,请参考老猿前面的博文《第7.5节 揭开Python类中self的面纱》
      2)name:需要定义的属性名字,为字符串类型。注意属性不仅是实例变量,也可能是实例方法,但不会是类变量,因为触发该方法只能通过“对象.属性”触发,而在使用该方式给类变量赋值时,并不是赋值给类变量,而是定义了一个与类变量同名的实例变量。具体请参考老猿前面的博文《第7.12节 可共享的Python类变量》
      3)value:属性需要设置的值;
    3. 重写一个类的实例方法__setattr__后,在该类的任何个属性被尝试赋值时将调用重写的方法,这个调用会取代正常机制(即将值保存到实例字典)。因此在自定义类中重写__setattr__方法后,如果需要继续执行赋值给一个实例属性的任务,它应该调用同名的父类方法,例如 object.__setattr__方法, 或执行“实例.dict[属性名]=值”,如果调用自身的__setattr__方法执行赋值语句,会导致__setattr__方法的嵌套递归调用。

    三、 案例

    1. 案例说明
      1)定义类Car,类中除了构造方法,还有实例方法drive1和重写的__setattr__;
      2)重写的__setattr__方法就是在调用时输出相关属性及其要设置的值,并为了保障属性值正常设置,调用了父类的__setattr__方法去设置属性的值;
      3)在类体外定义了实例car;
      4)在类体外定义一个函数fdrive,在定义实例后,将该函数动态赋值给类的实例变量drive2,并通过MethodType实现实例与该函数的绑定;
      5)执行相关方法查看重写的__setattr__方法的执行情况以及对__dict__的影响。

    2. 案例代码

    >>> from types import MethodType #导入MethodType方法
    >>> def fdrive(self,distance):
        print(f"In function fdrive:distance={distance}")
        self.totaldistance += distance  #定义一个函数,准备用于赋值给类的实例方法属性
        
    >>> class Car():
        def __init__(self, power):
            self.power = power
            self.totaldistance=0
            
        def drive1(self,distance):
            print(f"In method drive:distance={distance}")
            self.totaldistance+=distance
            
        def __setattr__(self,name,value):
            print(f"execute __setattr__:name={name},value={value}")
            self.__dict__[name]= value
          
    >>> car = Car('汽油发动机')
    execute __setattr__:name=power,value=汽油发动机
    execute __setattr__:name=totaldistance,value=0
    >>> car.drive1(100)
    In method drive:distance=100
    execute __setattr__:name=totaldistance,value=100
    >>> car.__setattr__('drive2',MethodType(fdrive,car)) #用函数定义实例方法drive2
    execute __setattr__:name=drive2,value=<bound method fdrive of <__main__.Car object at 0x00000000045E5A90>>
    >>> car.drive2(100)
    In function fdrive:distance=100
    execute __setattr__:name=totaldistance,value=200
    >>> car.__dict__  #查看自定义实例变量
    {'power': '汽油发动机', 'totaldistance': 200, 'drive2': <bound method fdrive of <__main__.Car object at 0x00000000045E5A90>>}
    >>>
    
    1. 执行截图

    在这里插入图片描述
    5. 案例总结
    1)通过上述执行情况可以确认,无论是类体内的实例方法(含构造方法)执行还是类体外的实例变量赋值,都会触发__setattr__方法的执行。因此该自定义方法可以完全捕获所有对属性的设置,如果需要加控制逻辑可以在该自定义方法中增加;
    2)动态绑定的实例方法,与类体内定义的实体方法不同,动态定义的在__dict__中有一个实例变量,而类体内预定义的则没有。这是因为动态定义本身就是一个赋值语句,肯定会触发实例变量的增加。

    本节结合案例介绍了重写__setattr__方法的实现以及相关注意事项,通过重写类的实该方法__setattr__,可以捕获对该类的所有属性定义和赋值。

    老猿Python,跟老猿学Python!
    博客地址:https://blog.csdn.net/LaoYuanPython

    请大家多多支持,点赞、评论和加关注!谢谢!

  • 相关阅读:
    mysql 中表和数据库名称不要使用 '-' 命名
    htmlElement.style 是只读属性
    chrome 远程调试相关问题
    纯小白入手 vue3.0 CLI
    纯小白入手 vue3.0 CLI
    纯小白入手 vue3.0 CLI
    《前端之路》- TypeScript (四) class 中各类属性、方法,抽象类、多态
    《前端之路》- TypeScript (三) ES5 中实现继承、类以及原理
    《前端之路》- TypeScript(二) 函数篇
    《前端之路》--- 重温 Egg.js
  • 原文地址:https://www.cnblogs.com/LaoYuanPython/p/13643711.html
Copyright © 2011-2022 走看看