python魔术方法二
反射
- 概述
运行时,runtime
区别于编译时,指的是程序被加载到内存中执行的时候。反射reflection
,指的是运行时获取类型定义信息。
一个对象能够在运行时,像照镜子一样,反射出其类型信息。
简单说,在Python中,能够通过一个对象,找出其type
,class
,attribute
或method
的能力,称为反射或者自省。
具有反射能力的函数有type()
,isinstance()
,callable()
,dict()
,getattr()
内建函数 | 意义 |
---|---|
getattr(object,name[,default]) |
通过name 返回object 的属性值。当属不存在,将使用default 返回,如果没有default , 则抛出AttributeError ,name 必须为字符串 |
setattr(object,name,value) |
object 的属性存在,则覆盖,不存在,新增 |
hasattr(object,name) |
判断对象是否有这个名字的属性, name 必须为字符串 |
# __getattr__ __setattr__ __delattr__ __getattribute__
class Point:
def __init__(self,x,y) -> None:
self.x = x
self.y = y
def showme(self):
return '<{}:{}>'.format(self.x,self.y)
t = Point(1,2)
print(t.x,t.y)
print(t.showme,t.showme())
print(getattr(t,'x'))
print(getattr(t,'y'))
print(t.y)
# 在外面为类增加一个属性
setattr(Point,'showme',
lambda self: '< --- {}:{} --->'.format(self.x,self.y)
)
print(t.showme()) # < --- 1:2 --->
# 看有没有这个属性
print(hasattr(t,'x')) # True
print(hasattr(t,'z')) # False
class Point:
# z = 100 #假如这里有z,那么就不会调用__getattr__方法,print(t.z) # 100
def __init__(self,x,y) -> None:
self.x = x
self.y = y
def showme(self):
return '<{}:{}>'.format(self.x,self.y)
def __getattr__(self,name):
return 'no such attr {}'.format(name)
t = Point(2,2)
print(t.x) # 2
print(t.y) # 2
print(t.z) # no such attr z # 先是实例的字典中有没有z,mro 中从左到右所有类开始找,找到了就返回,找不到就调用__getattr__方法
# __setattr__ 当设置一个属性的时候,会调用这个方法: self.x = x或setattr(t,'x',x)调用,解决方法1.调用object同名的方法 super().__setattr__(key,value) 2.使用自己的实例字典 self.__dict__[key] = value
class Point:
def __init__(self,x,y) -> None:
self.x = x
self.y = y
def __setattr__(self,key , value):
print('setattr ---')
# super().__setattr__(key,value) # 自己解决不了的时候,调用父类的方法
self.__dict__[key] = value # 调用字典
t = Point(1,2)
print(t.x) # 1
# __delattr__ del 实例.xxx 都会触发魔术方法__delattr__的调用
class Point:
# z = 100 # 当 del Point.z 只要通过实例删除,都会调用__delattr__方法
def __init__(self,x,y) -> None:
self.x = x
self.y = y
def __delattr__(self,item):
print('del {} ---'.format(item))
super().__delattr__(item)
t = Point(1,2)
print(1, t.x)
del t.x
print(t.__dict__) # {'y': 2} # 删除了x属性
# del x ---
# {'y': 2}
# __getattribute__ 属性访问第一站, 当访问一个属性的时候,会调用这个方法,返回值就是这个属性的值
class Point:
def __init__(self,x,y) -> None:
self.x = x
self.y = y
def __getattribute__(self,item):
print('getattribute ---')
# return super().__getattribute__(item)
ret = object.__getattribute__(self,item)
return ret * 10
t = Point(1,2)
print(t.x) # 10
# __getattribute__ 属性访问第一站, 一般建议不要定义它,如果定义了,为了正常访问属性,需要调用父类的方法,以免陷入死循环
class Point:
def __init__(self,x,y) -> None:
self.x = x
self.y = y
def __getattr__(self, item):
return self.__dict__[item] * 10
def __getattribute__(self,item):
print('getattribute ---')
if item != '__dict__':
raise AttributeError('no such attr {}'.format(item))
return super().__getattribute__(item)
t = Point(1,2)
print(t.x) # 10
- 总结
魔术方法 | 意义 |
---|---|
__getattr__() |
当通过搜索实例、实例的类及祖先类查不到属性时,就会调用此方法。 |
__setattr__() |
通过. 访问实例属性,进行增加、修改都要调用它。 |
__delattr__() |
当通过实例来删除属性时调用此方法。 |
__getattribute__ |
实例所有的属性调用都从这个方法开始。 |
实例属性查找顺序
实例调用__getattribute__()
—> instance.__dict__
—> instance.__class__.__dict__
—> 继承的祖先类(直到object
)的__dict__
—> 调用__getattr__()