程序员文章、书籍推荐和程序员创业信息与资源分享平台

网站首页 > 技术文章 正文

python委托定制超类getattr和getattribute管理属性

hfteth 2025-06-15 16:27:46 技术文章 4 ℃

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
 姓:李
 员工信息:姓名=李四,职位=经理,薪水=60000

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
 姓:李
 员工信息:姓名=李四,职位=经理,薪水=60000

1.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
 姓:李
 员工信息:姓名=李四,职位=经理,薪水=60000

python3.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-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)

# 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 65001
Active code page: 65001

C:\Users\Administrator>D:\Python27\python.exe E:\documents\F盘\managerstaff.py
2.7.18
姓:张
员工信息:姓名=张三,职位=开发,薪水=110000
Manager.__getattribute__ 拦截属性:staff
True
Manager.__getattribute__ 拦截属性:surName
Manager.__getattribute__ 拦截属性:staff
姓:李
Manager.__getattribute__ 拦截属性:giveRaise
Manager.__getattribute__ 拦截属性:staff
员工信息:姓名=李四,职位=经理,薪水=60000

python3.x执行结果

C:\Users\Administrator>D:\Python3\python.exe E:\documents\F盘\managerstaff.py
3.7.8
姓:张
员工信息:姓名=张三,职位=开发,薪水=110000
Manager.__getattribute__ 拦截属性:staff
True
Manager.__getattribute__ 拦截属性:surName
Manager.__getattribute__ 拦截属性:staff
姓:李
Manager.__getattribute__ 拦截属性:giveRaise
Manager.__getattribute__ 拦截属性:staff
员工信息:姓名=李四,职位=经理,薪水=60000

1.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-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)

# 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 65001
Active code page: 65001

C:\Users\Administrator>D:\Python27\python.exe E:\documents\F盘\managerstaff.py
2.7.18
姓:张
员工信息:姓名=张三,职位=开发,薪水=110000
Manager.__getattribute__ 拦截属性:staff
True
Manager.__getattribute__ 拦截属性:surName
Manager.__getattribute__ 拦截属性:staff
姓:李
Manager.__getattribute__ 拦截属性:giveRaise
Manager.__getattribute__ 拦截属性:staff
<__main__.Manager object at 0x0000000002F97508>

python3.x执行结果

C:\Users\Administrator>D:\Python3\python.exe E:\documents\F盘\managerstaff.py
3.7.8
姓:张
员工信息:姓名=张三,职位=开发,薪水=110000
Manager.__getattribute__ 拦截属性:staff
True
Manager.__getattribute__ 拦截属性:surName
Manager.__getattribute__ 拦截属性:staff
姓:李
Manager.__getattribute__ 拦截属性:giveRaise
Manager.__getattribute__ 拦截属性:staff
<__main__.Manager object at 0x01888F70>

2 END

本文首发微信公众号:梯阅线条

更多内容参考python知识分享或软件测试开发目录。

Tags:

最近发表
标签列表