在运行时,通过反射可以获得一个对象的名字,类型等一切特征信息。或者自省。
反射相关的内建函数
# encoding = utf-8 __author__ = "wwww.mcabana.com" class Point: def __init__(self, x, y): self.x = x self.y = y p1 = Point(2, 5) print(p1.__dict__) print(p1.x, p1.y) print(getattr(p1, 'x', 20)) # 打印p1.x, 如果不存在则p1.x = 30 print(getattr(p1, 'y', 50)) # 打印p1.y, 如果不存在则p1.y = 50 p1.x = 10 setattr(p1, 'x', 100) # 同上 p1.y = 20 setattr(p1, 'y', 200) # 同上 p1.z = 30 if not hasattr(p1, 'z'): # 如果 p1.z 不存在,则 p1.z = 300 setattr(p1, 'z', 300) print(p1.__dict__) print(p1.__dict__['x']) # 通过 __dict__ 直接访问 p1.__dict__['x'] = 1 print(p1.x) getattr(p1, '__dict__')['x'] = 100 print(p1.x)
反射相关的 魔术方法
__getattr__()
# encoding = utf-8 __author__ = "www.mcabana.com" class Point: def __init__(self, x, y): self.x = x self.y = y def __getattr__(self, item): # 访问不存在的属性,会到这里,结果就是return值,就不会抛出异常了 setattr(self, item, 100) # 可以设置一个新的 #return 100 # 也可以返回一个默认值, return返回的不会出现在 dict 中 #return "{} missing".format(item) p1 = Point(4, 5) print(p1.x, p1.y, p1.z) print(p1.__dict__)
所以实例属性访问顺序发生了变化,按照mro找不到,最后会去 __getatttr__() 找,如果没有__getattr__() 则抛出异常。
实例属性查找顺序为:
instance.__dict__ --> instance.__class__.__dict__ --> 继承的祖先类(直到object)的__dict__ ---找不到--> 调用__getattr__() |
__setattr__()
__setattr__() 会影响实例的赋值顺序。
通过.来访问修改实例属性,都会访问它。
实例初始化定义属性时,会调用__setattr__()
如果__setattr__() 中没有定义属性,__init__()中的属性是不生效的,dict字典也是空的。
如果在__setattr__() 中,使用setattr() 定义属性,setattr() 会再去调用__setattr__() ,这样就会陷入一个死循环,为了避免这种死循环python定义了一个异常 RecursionError,递归到一定深度,就会抛出。避免了栈的崩溃。
所以,调用__setattr__() 的正确方式应该是:
# encoding = utf-8 __author__ = "www.mcabana.com" class Point: def __init__(self, x, y): self.x = x self.y = y print('init ~~~~~~~~~~~~~~') def __setattr__(self, key, value): print('setattr ~~~~~~~') #setattr(self, key, value) self.__dict__[key] = value # 通过操作实例的 dict 进行属性定义 p1 = Point(4, 5) print(p1.__dict__) print(p1.x, p1.y)
__delattr__()
通过实例删除属性,会调用__delattr__() 魔术方法
__getattribute__() --- 了解
所有通过实例访问的属性,都要经过 __getattribute__()
上面的例子,虽然没有直接get,但是setattr定义的过程,其实就在访问实例的属性了。
所以在__getattribute__()中,可以对实例属性做访问控制,通过Object.__getattribute__() 获得属性。
# encoding = utf-8 __author__ = "www.mcabana.com" class Point: def __init__(self, x, y): self.x = x self.y = y print('init ~~~~~~~~~~~~~~') def __setattr__(self, key, value): print('setattr ~~~~~~~') #setattr(self, key, value) self.__dict__[key] = value # 通过操作实例的 dict 进行属性定义,也要经过 __getattribute__() def __getattribute__(self, item): # 所有通过实例访问的属性,都要先经过这里 print('getattribute ~~~~~~~~~') return object.__getattribute__(self, item) # 在Point中找不到,就去它的父类中去找 #return super().__getattribute__(item) # 同上 p1 = Point(4, 5) print(p1.__dict__) print(p1.x, p1.y)
结合raise
# encoding = utf-8 __author__ = "www.mcabana.com" class Point: def __init__(self, x, y): self.x = x self.y = y print('init ~~~~~~~~~~~~~~') # def __setattr__(self, key, value): # print('setattr ~~~~~~~') # #setattr(self, key, value) # self.__dict__[key] = value # 通过操作实例的 dict 进行属性定义,也要经过 __getattribute__() def __getattr__(self, item): print('getattr ~~~~~~~~') def __getattribute__(self, item): # 所有通过实例访问的属性,都要先经过这里 print('getattribute ~~~~~~~~~') #return object.__getattribute__(self, item) # 在Point中找不到,就去它的父类中去找 #return super().__getattribute__(item) # 同上 raise AttributeError p1 = Point(4, 5) print(p1.__dict__) print(p1.x, p1.y)
实例属性查找顺序:
https://www.hugbg.com/archives/2675.html
评论