41.__setattr__、__setitem__

__setattr__、__setitem__

1. __setattr__(self, item, value):
会拦截所有属性的的赋值语句,如果定义了这个方法,在给属性变量赋值时会调用__setattr__(self, item, value)方法,执行self.__dict__[key] = value。当在__setattr__(self, item, value)方法内对属性进行赋值时,不可使用self.name = value,因为他会再次调用__setattr__(self, item, value)方法形成无限循环,最后导致堆栈溢出异常。应该通过对属性字典做索引运算来赋值任何实例属性,也就是使用self.__dict__['name'] = value.
2. __setitem__(self, key, value):
同setattr方法类似,会拦截所有属性的赋值语句,区别在于如果将对象当作字典操作,设置键值对时会触发该方法,同样在__setitem__(self, key, value)方法内对属性进行赋值时,也不能使用self.name = value,而应该使用self.__dict__['name'] = value.
class Student():
    def __setattr__(self, key, value):
        print('调用了setattr')
        self.__dict__[key] = value

    def __setitem__(self, key, value):
        print('调用了setitem')
        self.__dict__[key] = value

s = Student()
s.age = 1  # 调用__setattr__ 方法
s['name'] = 'tom'  # 调用 __setitem__ 方法
class Foo:
    def __init__(self):
        self.__data = {}

    def __setitem__(self, key, value):  # 对象像字典那样操作
        self.__data[key] = value

    def __getitem__(self, item): return self.__data[item]

    def __delitem__(self, key): del self.__data[key]

    def __getattr__(self, item):
        print(item)

    def __setattr__(self, key, value):  # 方法重写
        print(key, value)
        super().__setattr__(key, value)

    def __delattr__(self, item):  # 方法重写
        print(item)
        super().__delattr__(item)

f = Foo()
f['name'] = 'xuan'
f['age'] = 8
f['hobby'] = 'eatting'
f.m = 'hello'
f.school = 'jiue'
print(f.__dict__)
_Foo__data {}
m hello
school jiue
{'_Foo__data': {'name': 'xuan', 'age': 8, 'hobby': 'eatting'}, 'm': 'hello', 'school': 'jiue'}