__getattr__和__getattribute__和__s

2018-07-04  本文已影响0人  MononokeHime

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}

注意:

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
上一篇 下一篇

猜你喜欢

热点阅读