8.魔术方法(3)
2021-01-10 本文已影响0人
Stone_説
目录:
1.反射概述
2.反射相关的函数和方法
3.发射的魔术方法
1.反射概述
运行时,区别于编译时,指的是程序被加载到内存中执行的时候。 反射,reflection,指的是运行时获取类型定义信息。
一个对象能够在运行时,像照镜子一样,反射出其类型信息。简单说,在Python中,能够通过一个对象,找出其type,class,attribute或method的能力,称为反射或者自省。
具有反射能力的函数有type(),isinstance(),callable(),dir(),getattr()等
2.反射相关的函数和方法
例1:
class Point:
def __init__(self,x,y):
self.x = x
self.y = y
def __str__(self):
return "Point({},{})".format(self.x,self.y)
def show(self):
print(self.x,self.y)
p = Point(4,5)
print(p)
Point(4,5)
print(p.__dict__)
{'x': 4, 'y': 5}
p.__dict__['y'] = 16
print(p.__dict__)
{'x': 4, 'y': 16}
p.z = 10
print(p.__dict__)
{'x': 4, 'y': 16, 'z': 10}
print(dir(p))
[
'__class__',
'__delattr__',
'__dict__',
...
'show',
'x',
'y',
'z'
]
print(p.__dir__())
[
'x',
'y',
'z',
'__module__',
...
'__class__'
]
例2:
例1通过属性字典dict来访问对象的属性,本质上也是利用的反射的能力。但是,访问方式不友好,Python提供了内置的函数。
1.getattr(object,name[,default])
通过name返回object的属性值,当属性不存在时,将使用default返回,如果没有default,则抛出AttributeError。name必须为字符串。
2.setattr(object,name,value)
object的属性存在,则覆盖,不存在,新增
3.hasattr(object,name)
判断对象是否有这个名字的属性,name必须为字符串
class Point:
def __init__(self,x,y):
self.x = x
self.y = y
def __str__(self):
return "Point({},{})".format(self.x,self.y)
def show(self):
print(self.x,self.y)
p1 = Point(4,5)
p2 = Point(10,10)
print(repr(p1),repr(p2),sep='\n')
<__main__.Point object at 0x0000012DFFBE6048>
<__main__.Point object at 0x0000012DFFBE6080>
print(p1.__dict__)
setattr(p1,'y',16)
setattr(p1,'z',10)
print(getattr(p1,'__dict__'))
{'x': 4, 'y': 5}
{'x': 4, 'y': 16, 'z': 10}
# 动态调用方法
if hasattr(p1,'show'):
getattr(p1,'show')()
4 16
# 动态增加方法
if not hasattr(Point,'add'):
setattr(Point,'add',lambda self,other:Point(self.x + other.x,self.y+other.y))
print(Point.add)
print(p1.add)
print(p1.add(p2))
<function <lambda> at 0x0000029957622EA0>
<bound method <lambda> of <__main__.Point object at 0x00000299578050F0>>
Point(14,26)
# 为实例增加方法,未绑定
if not hasattr(p1,'sub'):
setattr(p1,'sub',lambda self,other:Point(self.x - other.x,self.y - other.y))
print(p1.sub(p1,p1))
print(p1.sub)
Point(0,0)
<function <lambda> at 0x0000024E87450BF8>
# add在谁里面,sub在谁里面
print(p1.__dict__)
{
'x': 4,
'y': 16,
'z': 10,
'sub': <function <lambda> at 0x0000024E87450BF8>
}
print(Point.__dict__)
{
'__module__': '__main__',
'__init__': <function Point.__init__ at 0x0000024E87450A60>,
'__str__': <function Point.__str__ at 0x0000024E87450AE8>,
'show': <function Point.show at 0x0000024E87450B70>,
'__dict__': <attribute '__dict__' of 'Point' objects>,
'__weakref__': <attribute '__weakref__' of 'Point' objects>,
'__doc__': None,
'add': <function <lambda> at 0x0000024E87272EA0>
}
3.反射相关的魔术方法
魔术方法 含义
__getattr__ 当通过搜索实例、实例的类及祖先类查不到的属性,就会调用此方法
__setattr__ 通过.访问实例属性,进行增加、修改都要调用它
__delattr__ 当通过实例来删除属性时调用此方法
__getattribute__ 实例所有的属性调用都从这个方法开始
3.1 __getattr__
例1:
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 __getattr__(self, item):
return "missing {}".format(item)
p1 = Point(4,5)
print(p1.x)
print(p1.z)
print(p1.n)
print(p1.t)
# 运行结果
4
6
0
missing t
# 实例属性会按照继承关系找,如果找不到,就会执行__getattr__()方法,如果没有这个方法,就会抛出AttributeError异常表示找不到属性
查找啊属性顺序为:
instance.__dict__ --> instance.__class__.__dict__ --> 继承的祖先类(直到object)的__dict__--找不到-->调用__getattr__()
3.2 __setattr__
例1:
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 __getattr__(self, item):
return "missing {}".format(item)
def __setattr__(self, key, value):
print("setattr {} = {}".format(key,value))
p1 = Point(4,5)
print(p1.x)
print(p1.z)
print(p1.n)
print(p1.t)
p1.x = 50
print(p1.x)
print(p1.__dict__)
p1.__dict__['x'] = 60
print(p1.__dict__)
print(p1.x)
# 执行结果
setattr x = 4
setattr y = 5
missing x
6
0
missing t
setattr x = 50
missing x
{}
{'x': 60}
60
# 实例通过.点号设置属性,例如self.x = x属性赋值,就会调用__setattr__(),属性要加到实例的__dict__中,就需要自己完成
例2:
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 __getattr__(self, item):
return "missing {}".format(item)
def __setattr__(self, key, value):
print("setattr {} = {}".format(key,value))
self.__dict__[key] = value
p1 = Point(4,5)
print(p1.x)
print(p1.z)
print(p1.n)
print(p1.t)
p1.x = 50
print(p1.x)
print(p1.__dict__)
p1.__dict__['x'] = 60
print(p1.__dict__)
print(p1.x)
# 运行结果
setattr x = 4
setattr y = 5
4
6
0
missing t
setattr x = 50
50
{'x': 50, 'y': 5}
{'x': 60, 'y': 5}
60
#__setattr__()方法,可以拦截对实例属性的增加,修改操作,如果要设置生效,需要自己操作实例的__dict__
例3:
__getattr__和__setattr__综合使用
class B:
b = 200
class A(B):
z = 100
d = {}
def __init__(self,x,y):
self.x = x
setattr(self,'y',y)
self.__dict__['a'] = 5
def __getattr__(self, item):
print('~~~~~~~~~~~~',item)
return self.d[item]
def __setattr__(self, key, value):
print(key)
print(value)
self.d[key] = value
def __delattr__(self, item):
print('can not del {}'.format(item))
a = A(4,5)
print(a.__dict__)
print(A.__dict__)
print(a.x,a.y)
print(a.a)
# 运行结果
x
4
y
5
{'a': 5}
{
'__module__': '__main__',
'z': 100,
'd': {'x': 4, 'y': 5},
'__init__': <function A.__init__ at 0x0000027293970A60>,
'__getattr__': <function A.__getattr__ at 0x0000027293970AE8>,
'__setattr__': <function A.__setattr__ at 0x0000027293970B70>,
'__delattr__': <function A.__delattr__ at 0x0000027293970BF8>,
'__doc__': None
}
~~~~~~~~~~~~ x
~~~~~~~~~~~~ y
4 5
5
3.3 __delattr__
可以阻止通过实例来删除属性的操作,但是通过类依然可以删除属性
class Point:
Z = 5
def __init__(self,x,y):
self.x = x
self.y = y
def __delattr__(self, item):
print('Can not del {}'.format(item))
p = Point(4,5)
del p.x
p.z = 5
del p.z
del p.Z
print(Point.__dict__)
print(p.__dict__)
del Point.Z
print(Point.__dict__)
# 运行结果
Can not del x
Can not del z
Can not del Z
{
'__module__': '__main__',
'Z': 5,
'__init__': <function Point.__init__ at 0x0000025D783B0A60>,
'__delattr__': <function Point.__delattr__ at 0x0000025D783B0AE8>,
'__dict__': <attribute '__dict__' of 'Point' objects>,
'__weakref__': <attribute '__weakref__' of 'Point' objects>,
'__doc__': None
}
{'x': 4, 'y': 5, 'z': 5}
{
'__module__': '__main__',
'__init__': <function Point.__init__ at 0x0000025D783B0A60>,
'__delattr__': <function Point.__delattr__ at 0x0000025D783B0AE8>,
'__dict__': <attribute '__dict__' of 'Point' objects>,
'__weakref__': <attribute '__weakref__' of 'Point' objects>,
'__doc__': None
}
3.4 __getattribute__
例1:
class Base:
n = 0
class Point(Base):
z = 6
def __init__(self,x,y):
self.x = x
self.y = y
def __getattr__(self, item):
return "missing {}".format(item)
def __getattribute__(self, item):
return item
p1 = Point(4,5)
print(p1.__dict__)
print(p1.x,p1.z,p1.n,p1.t)
print(Point.__dict__)
print(Point.z)
# 运行结果
__dict__
x z n t
{
'__module__': '__main__',
'z': 6,
'__init__': <function Point.__init__ at 0x000001F41B960AE8>,
'__getattr__': <function Point.__getattr__ at 0x000001F41B960B70>,
'__getattribute__': <function Point.__getattribute__ at 0x000001F41B960BF8>,
'__doc__': None
}
6
# 实例的所有的属性访问,第一个都会调用__getattribute__方法,它阻止了属性的查找,该方法应该返回(计算后的)值或者抛出一个AttributeError异常。
它的return值将作为属性查找的结果
如果抛出AttributeError异常,则会直接调用__getattr__方法,因为表示属性没有找到
例2:
class Base:
n = 0
class Point(Base):
z = 6
def __init__(self,x,y):
self.x = x
self.y = y
def __getattr__(self, item):
return "missing {}".format(item)
def __getattribute__(self, item):
return object.__getattribute__(self,item)
p1 = Point(4,5)
print(p1.__dict__)
print(p1.x,p1.z,p1.n,p1.t)
print(Point.__dict__)
print(Point.z)
# 运行结果
{'x': 4, 'y': 5}
4 6 0 missing t
{
'__module__': '__main__',
'z': 6,
'__init__': <function Point.__init__ at 0x000001C13E300AE8>,
'__getattr__': <function Point.__getattr__ at 0x000001C13E300B70>,
'__getattribute__': <function Point.__getattribute__ at 0x000001C13E300BF8>,
'__doc__': None
}
6
# __getattribute__方法中为了避免在该方法中无限的递归,它的实现应该永远调用基类的同名方法以访问需要的任何属性,例如:object.__getattribute__(self,name)
注意:除非明确__getattribute__方法用来干什么,否则不要使用它