1 python委托定制超类getattr和getattribute管理属性
将一个类的实例属性通过self.attr=另一个类名(),赋值为另一个类对象,并且通过getattr和getattribute拦截属性来访问另一个类的属性,称为委托定制超类。
在_getattr__()中,通过getattr()返回超类实例对象的属性,从而实现超类实例属性的访问。
在__getattribute__()中,如果是本类属性通过object.__getattribute__()返回属性,避免循环;
在__getattribute__()中,如果是非本类属性,通过getattr()返回超类实例对象的属性,从而实现超类实例属性的访问。
1.1 通过getattr访问超类属性
用法
class MyClass: def __init__(self): # 将超类对象赋值给实例属性attr,通过委托定制超类 self.instance=超类名() def __getattr__(self,attr): # 拦截未定义属性的点号运算 # 返回被委托对象属性 print('MyClass 拦截未定义属性',attr) return getattr(self.instance,attr)描述
(1) 本类实例属性赋值为超类实例对象;
(2) 本类定义__getattr__()拦截未定义属性的点号运算;
(3) 在__getattr__()通过getattr()返回超类实例对象的属性,从而实现超类实例属性的访问。
示例
managerstaff.py
本例中,self.staff=Staff(),来实现定制超类。
# encoding=utf-8 import sys class Staff: def __init__(self,name,job=None,pay=0): self.name=name self.job=job self.pay=pay def surName(self): if sys.version.split()[0].startswith('2'): sn=self.name[:3] else: sn=self.name[0] return '姓:'+sn def giveRaise(self,rate): self.pay=int(self.pay*(1+rate)) def __str__(self): return '员工信息:姓名={0.name},职位={0.job},薪水={0.pay}'.format(self) class Manager: def __init__(self,name,pay): # 将Staff对象赋值给Manager的实例属性,通过委托定制超类 self.staff=Staff(name,'经理',pay) def giveRaise(self,rate,bonus=.10): self.staff.giveRaise(rate+bonus) def __getattr__(self,attr): # 拦截未定义属性的点号运算 # 返回被委托对象属性 print('Manager 拦截未定义属性:'+attr) return getattr(self.staff,attr) def __str__(self): # __str__ 必须返回 string , 需要用 str() 转换 return str(self.staff) if __name__=='__main__': import sys print(sys.version.split()[0]) s1=Staff('张三',job='开发',pay=100000) print(s1.surName()) s1.giveRaise(.10) print(s1) # Manager.__init__() m1=Manager('李四',50000) # 拦截未定义属性 surName,触发 Manager.__getattr__() # 触发 Staff.surName print(m1.surName()) # Manager.giveRaise -> Staff.giveRaise m1.giveRaise(.10) # Manager.__str__ -> Staff.__str__ print(m1)python3.x 执行结果
C:\Users\Administrator>D:\python3\python.exe E:\documents\F盘\managerstaff.py 3.7.8 姓:张 员工信息:姓名=张三,职位=开发,薪水=110000 Manager 拦截未定义属性 surName 姓:李 员工信息:姓名=李四,职位=经理,薪水=60000python2.x 执行结果
C:\Users\Administrator>chcp 65001 Active code page: 65001 C:\Users\Administrator>D:\Python27\python.exe E:\documents\F盘\managerstaff.py 2.7.18 姓:张 员工信息:姓名=张三,职位=开发,薪水=110000 Manager 拦截未定义属性:surName 姓:李 员工信息:姓名=李四,职位=经理,薪水=600001.2 getattr拦截print()情况
描述
getattr()拦截未定义属性,内置函数print()对应__str__()方法,当未定义__str__()时,print(实例名),python2.x会被拦截,python3.x不拦截。
NO | 调用方式 | 是否被__getattr__()拦截 |
1 | print(实例名) | python2.x被拦截 |
2 | print(实例名) | python3.x不拦截 |
示例
注释Manager的__str__()方法。
managerstaff.py
# encoding=utf-8 import sys class Staff: def __init__(self,name,job=None,pay=0): self.name=name self.job=job self.pay=pay def surName(self): if sys.version.split()[0].startswith('2'): sn=self.name[:3] else: sn=self.name[0] return '姓:'+sn def giveRaise(self,rate): self.pay=int(self.pay*(1+rate)) def __str__(self): return '员工信息:姓名={0.name},职位={0.job},薪水={0.pay}'.format(self) class Manager: def __init__(self,name,pay): # 将Staff对象赋值给Manager的实例属性,通过委托定制超类 self.staff=Staff(name,'经理',pay) def giveRaise(self,rate,bonus=.10): self.staff.giveRaise(rate+bonus) def __getattr__(self,attr): # 拦截未定义属性的点号运算 # 返回被委托对象属性 print('Manager 拦截未定义属性:'+attr) return getattr(self.staff,attr) #def __str__(self): # __str__ 必须返回 string , 需要用 str() 转换 #return str(self.staff) if __name__=='__main__': import sys print(sys.version.split()[0]) s1=Staff('张三',job='开发',pay=100000) print(s1.surName()) s1.giveRaise(.10) print(s1) # Manager.__init__() m1=Manager('李四',50000) # 拦截未定义属性 surName,触发 Manager.__getattr__() # 触发 Staff.surName print(m1.surName()) # Manager.giveRaise -> Staff.giveRaise m1.giveRaise(.10) # python2.x Manager.__getattr__() # python3.x object.__str__() print(m1) python2.x执行结果
C:\Users\Administrator>chcp 65001 Active code page: 65001 C:\Users\Administrator>D:\Python27\python.exe E:\documents\F盘\managerstaff.py 2.7.18 姓:张 员工信息:姓名=张三,职位=开发,薪水=110000 Manager 拦截未定义属性:surName 姓:李 员工信息:姓名=李四,职位=经理,薪水=60000python3.x执行结果
C:\Users\Administrator>D:\Python3\python.exe E:\documents\F盘\managerstaff.py 3.7.8 姓:张 员工信息:姓名=张三,职位=开发,薪水=110000 Manager 拦截未定义属性:surName 姓:李 <__main__.Manager object at 0x01AA8F30>1.3 通过getattribute访问超类属性
python的__getattribute__()拦截实例的全部属性的点号运算。
python2.x必须为新式类,getattribute方法才生效。
用法
# python2.x 的 __getattribute__ 对新式类生效,所以要(object) class MyClass(object): def __init__(self): # 将其它类对象赋值给实例属性instance,通过委托定制超类 self.instance=其它类名() def __getattribute__(self,attr): # 拦截全部属性的点号运算 print('MyClass.__getattribute__ 拦截属性:'+attr) if attr in ('attr1','attr2'): # 本类属性通过 object.__getattribute__ 返回,避免循环 return object.__getattribute__(self,attr) else: # 非本类属性通过 getattr(其它类对象,属性) 返回,实现访问超类属性 return getattr(self.instance,attr)描述
(1) python2.x本类显式声明为新式类(object);
(2) 本类实例属性赋值为其它类实例对象,实现委托定制超类;
(3) 本类定义__getattribute__()拦截属性的点号运算;
(4) 在__getattribute__()中,如果是本类属性通过object.__getattribute__()返回属性,避免循环;
(5) 在__getattribute__()中,如果是非本类属性,通过getattr()返回超类实例对象的属性,从而实现超类实例属性的访问。
(6) 在__str__()中,通过object.__getattribute__(self,超类对象的属性名),获取超类对象的字符串显示,实现访问超类的str()方法。
示例
managerstaff.py
# encoding=utf-8import sysclass Staff: def __init__(self,name,job=None,pay=0): self.name=name self.job=job self.pay=pay def surName(self): if sys.version.split()[0].startswith('2'): sn=self.name[:3] else: sn=self.name[0] return '姓:'+sn def giveRaise(self,rate): self.pay=int(self.pay*(1+rate)) def __str__(self): return '员工信息:姓名={0.name},职位={0.job},薪水={0.pay}'.format(self)# python2.x 的 __getattribute__ 对新式类生效,所以要(object) class Manager(object): def __init__(self,name,pay): # 将Staff对象赋值给Manager的实例属性,通过委托定制超类 self.staff=Staff(name,'经理',pay) def giveRaise(self,rate,bonus=.10): self.staff.giveRaise(rate+bonus) def __getattribute__(self,attr): # 拦截属性的点号运算 print('Manager.__getattribute__ 拦截属性:'+attr) if attr in ('staff','giveRaise'): # 本类属性通过 object.__getattribute__ 返回,避免循环 return object.__getattribute__(self,attr) else: # 非本类属性通过 getattr(其它类对象,属性) 返回,实现访问超类属性 return getattr(self.staff,attr) def __str__(self): # __str__ 必须返回 string , 需要用 str() 转换 staff=object.__getattribute__(self,'staff') return str(staff) if __name__=='__main__': import sys print(sys.version.split()[0]) s1=Staff('张三',job='开发',pay=100000) print(s1.surName()) s1.giveRaise(.10) print(s1) # Manager.__init__() m1=Manager('李四',50000) # 拦截未定义属性 surName,触发 Manager.__getattr__() # 触发 Staff.surName print(hasattr(m1.staff,'surName')) print(m1.surName()) # Manager.giveRaise -> Staff.giveRaise m1.giveRaise(.10) # Manager.__str__ -> Staff.__str__ print(m1)python2.x执行结果
C:\Users\Administrator>chcp 65001Active code page: 65001C:\Users\Administrator>D:\Python27\python.exe E:\documents\F盘\managerstaff.py2.7.18姓:张员工信息:姓名=张三,职位=开发,薪水=110000Manager.__getattribute__ 拦截属性:staffTrueManager.__getattribute__ 拦截属性:surNameManager.__getattribute__ 拦截属性:staff姓:李Manager.__getattribute__ 拦截属性:giveRaiseManager.__getattribute__ 拦截属性:staff员工信息:姓名=李四,职位=经理,薪水=60000python3.x执行结果
C:\Users\Administrator>D:\Python3\python.exe E:\documents\F盘\managerstaff.py3.7.8姓:张员工信息:姓名=张三,职位=开发,薪水=110000Manager.__getattribute__ 拦截属性:staffTrueManager.__getattribute__ 拦截属性:surNameManager.__getattribute__ 拦截属性:staff姓:李Manager.__getattribute__ 拦截属性:giveRaiseManager.__getattribute__ 拦截属性:staff员工信息:姓名=李四,职位=经理,薪水=600001.4 getattribute不拦截print()
描述
__getattribute__()拦截全部属性,内置函数print()对应__str__()方法,当print(实例名)时,python2.x和python3.x都不拦截。
NO | 调用方式 | 是否被__getattribute__()拦截 |
1 | print(实例名) | python2.x不拦截 |
2 | print(实例名) | python3.x不拦截 |
示例
注释Manager的str()方法。
managerstaff.py
# encoding=utf-8import sysclass Staff: def __init__(self,name,job=None,pay=0): self.name=name self.job=job self.pay=pay def surName(self): if sys.version.split()[0].startswith('2'): sn=self.name[:3] else: sn=self.name[0] return '姓:'+sn def giveRaise(self,rate): self.pay=int(self.pay*(1+rate)) def __str__(self): return '员工信息:姓名={0.name},职位={0.job},薪水={0.pay}'.format(self)# python2.x 的 __getattribute__ 对新式类生效,所以要(object) class Manager(object): def __init__(self,name,pay): # 将Staff对象赋值给Manager的实例属性,通过委托定制超类 self.staff=Staff(name,'经理',pay) def giveRaise(self,rate,bonus=.10): self.staff.giveRaise(rate+bonus) def __getattribute__(self,attr): # 拦截属性的点号运算 print('Manager.__getattribute__ 拦截属性:'+attr) if attr in ('staff','giveRaise'): # 本类属性通过 object.__getattribute__ 返回,避免循环 return object.__getattribute__(self,attr) else: # 非本类属性通过 getattr(其它类对象,属性) 返回,实现访问超类属性 return getattr(self.staff,attr) #def __str__(self): # __str__ 必须返回 string , 需要用 str() 转换 #staff=object.__getattribute__(self,'staff') #return str(staff) if __name__=='__main__': import sys print(sys.version.split()[0]) s1=Staff('张三',job='开发',pay=100000) print(s1.surName()) s1.giveRaise(.10) print(s1) # Manager.__init__() m1=Manager('李四',50000) # 拦截未定义属性 surName,触发 Manager.__getattr__() # 触发 Staff.surName print(hasattr(m1.staff,'surName')) print(m1.surName()) # Manager.giveRaise -> Staff.giveRaise m1.giveRaise(.10) # Manager.__str__ -> Staff.__str__ print(m1)python2.x执行结果
C:\Users\Administrator>chcp 65001Active code page: 65001C:\Users\Administrator>D:\Python27\python.exe E:\documents\F盘\managerstaff.py2.7.18姓:张员工信息:姓名=张三,职位=开发,薪水=110000Manager.__getattribute__ 拦截属性:staffTrueManager.__getattribute__ 拦截属性:surNameManager.__getattribute__ 拦截属性:staff姓:李Manager.__getattribute__ 拦截属性:giveRaiseManager.__getattribute__ 拦截属性:staff<__main__.Manager object at 0x0000000002F97508>python3.x执行结果
C:\Users\Administrator>D:\Python3\python.exe E:\documents\F盘\managerstaff.py3.7.8姓:张员工信息:姓名=张三,职位=开发,薪水=110000Manager.__getattribute__ 拦截属性:staffTrueManager.__getattribute__ 拦截属性:surNameManager.__getattribute__ 拦截属性:staff姓:李Manager.__getattribute__ 拦截属性:giveRaiseManager.__getattribute__ 拦截属性:staff<__main__.Manager object at 0x01888F70>2 END
本文首发微信公众号:梯阅线条,
更多内容参考python知识分享或软件测试开发目录。
