在运行时,通过反射可以获得一个对象的名字,类型等一切特征信息。或者自省。
反射相关的内建函数

# 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


评论