美文网首页
__getattr__和__getattribute__和__s

__getattr__和__getattribute__和__s

作者: MononokeHime | 来源:发表于2018-07-04 11:20 被阅读0次

    1.类属性和实例属性

    class Foo(object):
        a = 10  # 类属性
    
        @classmethod
        def foo_cls(cls):
            pass
    
        def __init__(self):
            self.b = 12  # 实例属性
    
        def mf(self):
            pass
    
    
    print(dir(Foo))
    print(Foo.__dict__)
    foo = Foo()
    print(dir(foo))
    print(foo.__dict__)
    foo.a = 100
    print(foo.__dict__)
    

    运行结果

    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'foo_cls', 'mf']
    {'__module__': '__main__', 'a': 10, 'foo_cls': <classmethod object at 0x104bef048>, '__init__': <function Foo.__init__ at 0x104beb620>, 'mf': <function Foo.mf at 0x104beb598>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'b', 'foo_cls', 'mf']
    {'b': 12}
    {'b': 12, 'a': 100}
    

    注意:

    • 类不可以调用实例属性
    • 对象调用属性的时候,先从对象的__dict__中寻找,如果有,直接返回;如果没有,则从类的__dict__中去找,如果有直接返回,如果没有,则报错
    • 每个对象创建都会有自己的__dict__,因此创建多个对象就会占用很大的内存空间,可以使用solt解决

    2.__getattr____getattribute__的使用

    当我们调用对象属性的时候,其实内部是隐式的调用魔法函数__getattr____getattribute__。执行顺序:

    1. 先执行__getattribute__(self,item)函数。该函数内部定义了属性的查找顺序,从对象.__dict__再到类.__dict__,一旦找到了就直接返回,不会执行__getattr__。一般不用重新定义,否则使用不当会出现循环递归。
    2. 如果__getattribute__没有找到属性,再执行__getattr__(self,item)函数。
    class Foo(object):
        a = 10
    
        def __init__(self):
            self.b = 12
    
        def __getattr__(self, item):
            print(item)
            return 120
        
        # def __getattribute__(self, item):
        #     print(item)
        #     return 20  # 一旦这么定义,所有的属性值都是20
    
    foo = Foo()
    print(foo.a) # 10。首先执行 __getattribute__函数,类.__dict__中包含了b属性,直接返回
    print(foo.b)  # 12。首先执行 __getattribute__函数,对象.__dict__中包含了b属性,直接返回
    print(foo.m) # m 120。__getattribute__函数没有找到m属性,所以继续调用__getattr__函数
    

    3.循环递归

    如果__getattribute__中调用了对象的属性,那么又会去执行__getattribute__魔法方法,这样就陷入了无穷无尽的递归当中。

    class Foo(object):
        a = 10
    
        def __init__(self):
            self.b = 12
    
        def __getattr__(self, item):
            print(item)
            return 120
    
        def __getattribute__(self, item):
            print(item)
            return self.b  # 等价于self.__getattribute__(item),陷入循环递归当中
    
    foo = Foo()
    print(foo.b)
    

    结果输出

    b
    b
    b
    .
    .
    .
    Traceback (most recent call last):
      File "/Users/Liang/Documents/PyProject/demo1.py", line 18, in <module>
        print(foo.b)
      File "/Users/Liang/Documents/PyProject/demo1.py", line 14, in __getattribute__
        return self.b
      File "/Users/Liang/Documents/PyProject/demo1.py", line 14, in __getattribute__
        return self.b
      File "/Users/Liang/Documents/PyProject/demo1.py", line 14, in __getattribute__
        return self.b
      [Previous line repeated 328 more times]
      File "/Users/Liang/Documents/PyProject/demo1.py", line 13, in __getattribute__
        print(item)
    RecursionError: maximum recursion depth exceeded while calling a Python object
    

    4.__setattr__的使用

    __setattr__方法

    会拦截所有属性的的赋值语句。如果定义了这个方法,self.arrt = value 就会变成self.__setattr__("attr", value).这个需要注意。当在__setattr__方法内对属性进行赋值是,不可使用self.attr = value,因为他会再次调用self.__setattr__("attr", value),则会形成无穷递归循环,最后导致堆栈溢出异常。应该通过对属性字典做索引运算来赋值任何实例属性,也就是使用self.__dict__['name'] = value

    class Foo(object):
    
        def __init__(self):
            print('enter init')
            self.m = 20
    
        def __setattr__(self, key, value):
            print('set',key,value)
            return self.__dict__.setdefault(key,value)
    
    
    foo = Foo()  # 打印 ‘enter init’和‘set m 20’。是因为self.m=10会调用__setattr__
    foo.n = 10  # 打印 ‘set m 10’
    print(foo.n) # 10
    

    相关文章

      网友评论

          本文标题:__getattr__和__getattribute__和__s

          本文链接:https://www.haomeiwen.com/subject/riceuftx.html