zoukankan      html  css  js  c++  java
  • python 自定义属性访问 __setattr__, __getattr__,__getattribute__, __call__

    object._getattr_(self, name)
    __gettattr__:如果某个类定义了这个方法,并且在该类的对象的字典中又找不到相应的属性时候,那么该方法会被调用。
    
    实例instance通过instance.name访问属性name,只有当属性name没有在实例的__dict__或它构造类的__dict__或基类的__dict__中没有找到,才会调用__getattr__。
    
    当属性name可以通过正常机制追溯到时,__getattr__是不会被调用的。如果在__getattr__(self, attr)存在通过self.attr访问属性,会出现无限递归错误。
    
    class ClassA(object):
        def __init__(self, classname):
            self.classname = classname
     
        def __getattr__(self, attr):
            return('invoke __getattr__', attr)
    insA = ClassA('ClassAname')
     
    print(insA.__dict__) # 实例insA已经有classname属性了
    # {'classname': 'ClassA'}
     
    print(insA.classname) # 不会调用__getattr__
    # ClassA
     
    print(insA.grade) # grade属性没有找到,调用__getattr__
    # ('invoke __getattr__', 'grade')
     
    print(insA.__dict__) # 实例insA已经有classname属性了
    # {'classname': 'ClassA'}
     
    #***********************************************************************
    print(getattr(insA,'classname'))
    #ClassAname
     
    print(getattr(insA,'grade'))
    # ('invoke __getattr__', 'grade')
     
    setattr(insA, "grade", "18")              #增加属性
     
    print(insA.__dict__)
    #{'classname': 'ClassAname', 'grade': '18'}
     
    print(insA.grade)
    #18
    注意下面这个例子,没有__getattr__,所以insA.grade时就报错了,所以还是加入该方法比较好
    
    insA.grade和 getattr(insA,'grade') 调用属性的方式是一样的
    
    注意:类中没有__getattr__方法,所以使用insA.grade和getattr(insA,'grade')都会报错
    
    所以:getattr(insA,'grade')同insA.grade是一个方法,如果没有__getattr__,则会报错!
    
    class ClassA():
        def __init__(self, classname):
            self.classname = classname
    insA = ClassA('ClassAname')
     
    print(insA.__dict__) # 实例insA已经有classname属性了
    # {'classname': 'ClassAname'}
     
    print(insA.classname) # 不会调用__getattr__
    # ClassAname
     
    print(insA.grade) # grade属性没有找到,调用__getattr__,但无__getattr__
    '''注意,报错了'''
    #AttributeError: 
    #'ClassA' object has no attribute 'grade'
     
    #***********************************************************
    print(getattr(insA,'classname'))
    #ClassAname
     
    print(getattr(insA,'grade'))
    '''注意,报错了'''
    #AttributeError: 
    #'ClassA' object has no attribute 'grade'
     
    setattr(insA, "grade", "18")              #增加属性
    print(insA.__dict__)
    #{'classname': 'ClassAname', 'grade': '18'}
     
    print(insA.grade)
    #18
    object.__getattribute__(self, name)
    __getattribute__:不管对象的字典中有没有找到对应的属性,都会调用
    
    实例instance通过instance.name访问属性name,__getattribute__方法一直会被调用,无论属性name是否追溯到。如果类还定义了__getattr__方法,除非通过__getattribute__显式的调用它,或者__getattribute__方法出现AttributeError错误,否则__getattr__方法不会被调用了。如果在__getattribute__(self, attr)方法下存在通过self.attr访问属性,会出现无限递归错误。
    
    如下所示,ClassA中定义了__getattribute__方法,实例insA获取属性时,都会调用__getattribute__返回结果,即使是访问__dict__属性。
    
    注意:
    
    print(insA.__dict__)
    # ('invoke __getattribute__', '__dict__')
    
    class ClassA(object):
     
        def __init__(self, classname):
            self.classname = classname
     
        def __getattr__(self, attr):
            return('invoke __getattr__', attr)
     
        def __getattribute__(self, attr):
            return('invoke __getattribute__', attr)
     
    insA = ClassA('ClassAname')
    print(insA.__dict__)
    # ('invoke __getattribute__', '__dict__')
     
    print(insA.classname)
    # ('invoke __getattribute__', 'classname')
     
    print(insA.grade)
    # ('invoke __getattribute__', 'grade')
    object.__setattr__(self, name, value)
    __setattr__:无论是直接赋值还是通过内置的setattr函数赋值,都会调用。。。基类一般会内置__setattr__()方法
    
    如果类自定义了__setattr__方法,当通过实例获取属性尝试赋值时,就会调用__setattr__。
    
    常规的对实例属性赋值,被赋值的属性和值会存入实例属性字典__dict__中。
    
    注意:这个类本身并没定义__setattr __()方法,应该是继承父类的,所以一切都很好,不建议加入该方法
    
     注意:可以显式调用 insA.__setattr__('tag1','insA1')
    
    class ClassA(object):
        def __init__(self, classname):
            self.classname = classname
     
    insA = ClassA('ClassA')
     
    print(insA.__dict__)
    # {'classname': 'ClassA'}
     
    insA.tag = 'insA'             #增加属性,正常
     
    print(insA.__dict__)
    # {'tag': 'insA', 'classname': 'ClassA'}
     
    #********************************************************
    setattr(insA, "age", "18")     #增加属性,正常
     
    print(insA.__dict__)
    #{'tag': 'insA', 'classname': 'ClassA', 'age': '18'}
     
    #********************************************************
    #显式调用
    insA.__setattr__('tag1','insA1')
    print(insA.__dict__)
    #{'tag1': 'insA1', 'tag': 'insA', 'classname': 'ClassA', 'age': '18'}
     
    #********************************************************
    #从基类继承了__setattr__()方法
    print(dir(insA))
    #['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__',
    #'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__',
    #'__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
    #'__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age',
    #'classname', 'tag', 'tag1']
    如下类自定义了__setattr__,对实例属性的赋值就会调用它。类定义中的self.attr也同样,所以在__setattr__下还有self.attr的赋值操作就会出现无线递归的调用__setattr__的情况。自己实现__setattr__有很大风险,一般情况都还是继承object类的__setattr__方法。 __ setattr __:无论是直接赋值还是通过内置的setattr函数赋值,都会调用。
    
    同时发现:
    
    insA.tag ='insA'与 setattr(insA,“age”,“18”)的定义方式相同 
    
    class ClassA(object):
        def __init__(self, classname):
            self.classname = classname
     
        def __setattr__(self, name, value):
            # self.name = value  # 如果还这样调用会出现无限递归的情况
            print('invoke __setattr__')
     
    insA = ClassA('ClassAname') # __init__中的self.classname调用__setattr__。
    # invoke __setattr__
     
    print(insA.__dict__)
    # {}                            #注意这里
     
    insA.tag = 'insA'    
    # invoke __setattr__
     
    print(insA.__dict__)
    # {}
     
    #********************************************************
    setattr(insA, "age", "18") 
    #invoke __setattr__
     
    print(insA.__dict__)
    # {}
    object.__delattr__(self, name)
    与__setattr __()类似,但用于删除属性而不是赋值。只有当del obj.name对对象有意义时才应该实现。
    
    object.__dir__(self)
    dir()作用在一个实例对象上时,__dir__会被调用。返回值必须是序列。dir()将返回的序列转换成列表并排序。
    
    object.__call__(self[, args...])
    当实例被“调用”为函数时调用; 如果定义了这个方法,x(arg1, arg2, ...)是x.__call__(arg1, arg2, ...)的速写版。
    
    Python中有一个有趣的语法,只要定义类型的时候,实现__call__函数,这个类型就成为可调用的。换句话说,我们可以把这个类的对象当作函数来使用,相当于重载了括号运算符。
    
    class Student(object):
        def __init__(self, name):
            self.name = name
        def __call__(self):
            print('My name is %s.' % self.name)
            
    s = Student('Michael')
    s()
    # My name is Michael.
    通过使用__setattr__, __getattr__, __delattr__可以重写dict,使之通过“.”调用键值。
    
    class Dict(dict):
        '''
        通过使用__setattr__,__getattr__,__delattr__
        可以重写dict,使之通过“.”调用
        '''
        def __setattr__(self, key, value):
            print("In '__setattr__")
            self[key] = value
            
        def __getattr__(self, key):
            try:
                print("In '__getattr__")
                return self[key]
            except KeyError as k:
                return None
                
        def __delattr__(self, key):
            try:
                del self[key]
            except KeyError as k:
                return None
                
        # __call__方法用于实例自身的调用,达到()调用的效果
        def __call__(self, key):    # 带参数key的__call__方法
            try:
                print("In '__call__'")
                return self[key]
            except KeyError as k:
                return "In '__call__' error"
                
    s = Dict()
    print(s.__dict__)
    # {}
     
    s.name = "hello"    # 调用__setattr__
    # In '__setattr__
     
    print(s.__dict__) # 由于调用的'__setattr__', name属性没有加入实例属性字典中。
    # {}
     
    print(s("name"))    # 调用__call__
    # In '__call__'
    # hello
     
    print(s["name"])    # dict默认行为
    # hello
     
    # print(s)
    print(s.name)       # 调用__getattr__
    # In '__getattr__
    # hello
     
    del s.name          # 调用__delattr__
    print(s("name"))    # 调用__call__
    

      

  • 相关阅读:
    JZOJ 3034. 【NOIP2012模拟10.17】独立集
    JZOJ 3035. 【NOIP2012模拟10.17】铁轨
    JZOJ 1259. 牛棚安排
    数位DP JZOJ 3316. 非回文数字
    JZOJ 3046. 游戏
    JZOJ 3013. 填充棋盘
    debian 安装oracle提供的java8
    java 汉字转拼音 PinYin4j
    debian ssh设置root权限登陆 Permission denied, please try again
    java并发下订单生成策略
  • 原文地址:https://www.cnblogs.com/lincappu/p/12606337.html
Copyright © 2011-2022 走看看