反射
运行时,区别于编译时,指的是程序被加载到内存中执行的时候。
反射,reflection,指的是运行时获取类型定义信息。
一个对象能够在运行时,像照镜子一样,反射出其类型信息。
简单说,在Python中,能够通过一个对象,找出其type、class、attribute或method的能力,称为反射或者自 省。
具有反射能力的函数有 type()、isinstance()、callable()、dir()、getattr()等
反射相关的函数和方法
需求
内建函数 | 意义 |
---|---|
getattr(object,name[,default]) | 通过name返回object的属性值,当属性不存在,将使用default返回,如果没有default,则抛出AttributeError.name必须为字符串 |
setattr(object,name,value) | object的属性存在,则覆盖,不存在,新增 |
hasattr(object,name) | 判断对象是否有这个名字的属性,name必须为字符串 |
class Point:
def __init__(self,x,y):
self.x = x
self.y = y
def __str__(self):
return '{},{}'.format(self.x,self.y)
def show(self):
print(self.x,self.y)
p = Point(3,4)
p2 = Point(5,6)
print(getattr(p,'z',1000))
#动态增加方法
#为类增加方法
if not hasattr(Point,'add'):
setattr(Point,'add',lambda self,other:Point(self.x+other.x,self.y+other.y))
print(p.add(p2))#绑定
#为实例增加方法,未绑定
if not hasattr(p,'sub'):
setattr(p,'sub',lambda self,other:Point(self.x-other.x,self.y-other.y))
print(p.sub(p2,p))
print(Point.__dict__)
print(p.__dict__)
这种动态增删属性的方式是运行时改变类或者实例的方式,但是装饰器或Mixin都是定义时就决定了,因此反射能力具有更大的灵活性.
反射相关的魔术方法
__getattr__(),__setattr__(),__delattr__()这三个魔术方法,分别测试者三个方法
class Base:
n = 0
class Point(Base):
z = 6
def __init__(self,x,y):
self.x = x
self.y = y
def show(self):
print(self.x,self.y)
def __getattribute__(self,item):
return object.__getattribute__(self,item)
def __getattr__(self,item):
return item
def __setattr__(self,key,value):
print('{}{}'.format(key,value))
self.__dict__[key] = value
p = Point(1,2)
print(p.x)
print(p.y)
print(p.__dict__)
print(p.z)
__getattr__()
实例属性会按照继承关系找,如果找不到,就会执行__getattr__()方法,如果没有这个方法,就会抛出AttributeError异常表示找不到属性
查找属性顺序为:
instance.__dict__ --> instance.__class__.__dict__ --> 继承的祖先类(直到object)的__dict__ —找不到–> 调 用__getattr__()
__setattr__()
实例通过.点号设置属性,例如self.x = x属性赋值,就会调用__setattr__(),属性要加到实例的__dict__中,就需要自己完成
__setattr__()方法,可以拦截对实例属性的增加 修改操作,如果要设置生效,需要自己操作实例的__dict__
__delattr__()
class Point():
Z = 6
def __init__(self,x,y):
self.x = x
self.y = y
def __delattr__(self,item):
print(item)
p = Point(1,2)
p.z = 10
del p.z
print(p.__dict__)
print(Point.__dict__)
del Point.Z
print(Point.__dict__)
可以阻止通过实例来删除属性的操作,但是通过类依然可以删除属性
__getattribute__
实例的所有的属性访问,第一个都会调用__getattribute__方法,它阻止了属性的查找,该方法应该返回(计算后的值)或者抛出一个AtteributeError异常
- 它的return值作为属性查找的结果
- 如果抛出AttributeError异常,则会直接调用 __getattr__ 方法,因为表示属性没有找到。
class Base:
n = 0
class Point(Base):
z = 6
def __init__(self,x,y):
self.x = x
self.y = y
def __getattribute__(self,item):
return object.__getattribute__(self,item)
def __getattr__(self,item):
return item
p = Point(1,2)
print(p.x)
print(p.y)
print(p.__dict__)
print(p.z)
__getattribute__方法中为了避免在该方法中无限的递归,它的实现应该永远调用基类的同名方法以访问需要的任何属性,例如object.__getattribute__(self,item)
注意,除非你明确地知道 __getattribute__ 方法用来做什么,否则不要使用它
魔术方法 | 意义 |
---|---|
__getattr__() | 当通过搜索实例、实例的类及祖先类查不到属性,就会调用此方法 |
__setattr__() | 通过 . 访问实例属性,进行增加、修改都要调用它 |
__delattr__ | 当通过实例来删除属性时调用此方法 |
__getattribute__ | 实例所有的属性调用都从这个方法开始 |
属性查找顺序
实例调用__getattribute__() --> instance.__dict__ --> instance.__class__.__dict__ --> 继承的祖先类(直到 object)的__dict__ --> 调用__getattr__()