32面向对象7_reflection
目录
reflection相关的内建函数:... 1
反射相关的魔术方法(__getattr__()、__setattr__()、__delattr__()):... 7
反射相关的魔术方法(__getattribute__):... 9
reflection反射
指运行时,获取类型定义信息;运行时通过实例能查出实例及所属类的类型相关信息;
一个对象能够在运行时,像照镜子一样,反射出它的所有类型信息;
简单说,在python中,能通过一个对象,找出其type、class、attribute、method的能力,称为反射或自省;
具有反射能力的函数有:type()、isinstance()、callable()、dir()、getattr();
注:
运行时,区别于编译时,指程序被加载到内存中执行的时候;
reflection相关的内建函数:
getattr(object,name[,default]),通过name返回object的属性值,当属性不存在,将使用default返回,如果没有default,则抛AttributeError,name必须为字符串;
setattr(object,name,value),object(实例或类)属性存在则覆盖,不存在新增;
hasattr(object,name),判断对象是否有这个名字的属性,name必须为字符串;
动态添加属性方式,与装饰器修改一个类、mixin方式的差异?
动态添加属性,是运行时改变类或者实例的方式,因此反射能力具有更大的灵活性;
装饰器和mixin,在定义时就决定了;
注:
一般运行时动态增,很少删;
self.__class__,同type(self);即实例a.__class__,同type(a);
例:
class Point:
def __init__(self,x,y):
self.x = x
self.y = y
def __str__(self):
return 'Point({},{})'.format(self.x,self.y)
__repr__ = __str__
def show(self):
print(self.x,self.y)
p = Point(4,5)
print(p.__dict__)
p.__dict__['y'] = 16
print(p.__dict__['y']) #通过实例的属性字典__dict__访问实例的属性,本质上是利用反射的能力,这种访问方式不优雅,python提供了相关的内置函数
p.z = 10
print(p.__dict__)
p1 = Point(4,5)
p2 = Point(10,10)
print(repr(p1),repr(p2))
print(p1.__dict__)
setattr(p1,'y',16)
setattr(p1,'z',18)
print(getattr(p1,'__dict__'))
if hasattr(p1,'show'): #动态调用方法
getattr(p1,'show')()
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))
输出:
{'x': 4, 'y': 5}
16
{'x': 4, 'y': 16, 'z': 10}
Point(4,5) Point(10,10)
{'x': 4, 'y': 5}
{'x': 4, 'y': 16, 'z': 18}
4 16
Point(14,26)
例:
class A:
def __init__(self,x):
self.x = x
a = A(5)
setattr(A,'y',10) #运行时改变属性,在类上操作
print(A.__dict__)
print(a.__dict__)
print(getattr(a,'x'))
print(getattr(a,'y')) #实例没有y,向上找自己类的
# print(getattr(a,'z')) #X
print(getattr(a,'z',100))
setattr(a,'y',1000) #在实例上操作
print(A.__dict__)
print(a.__dict__)
# setattr(a,'mtd',lambda self: 1) #在实例上定义方法,看似可以,实际不行,未绑定self,若要在调用时不出错,需把实际名写上,如a.mtd(a)
# a.mtd() #X
# print(a.mtd(a)) #V
print(a.__dict__)
setattr(A,'mtd',lambda self: 2) #在类上定义方法没问题
print(a.mtd())
print(A.__dict__)
输出:
{'__module__': '__main__', '__init__':
{'x': 5}
5
10
100
{'__module__': '__main__', '__init__':
{'x': 5, 'y': 1000}
{'x': 5}
2
{'__module__': '__main__', '__init__':
习题:
命令分发器,通过名称找对应的函数执行;
思路:名称找对象的方法;
函数方式实现:
方1:
def dispatcher():
cmds = {}
def reg(cmd,fn):
if isinstance(cmd,str):
cmds[cmd] = fn
else:
print('error')
def run():
while True:
cmd = input('plz input command: ')
if cmd.strip() == 'quit':
return
print(cmds.get(cmd.strip(),defaultfn)())
def defaultfn():
return 'default function'
return reg,run
reg,run = dispatcher()
reg('cmd1',lambda : 1)
reg('cmd2',lambda : 2)
run()
输出:
plz input command: cmd3
default function
plz input command: cmd2
2
plz input command: cmd1
1
plz input command: quit
方2:
def cmds_dispatcher():
cmds = {}
def reg(name):
def wrapper(fn):
cmds[name] = fn
return fn
return wrapper
def dispatcher():
while True:
cmd = input('plz input comd: ')
if cmd.strip() == 'quit':
return
print(cmds.get(cmd.strip(),defaultfn)())
def defaultfn():
return 'default function'
return reg,dispatcher
reg,dispatcher = cmds_dispatcher()
@reg('cmd1')
def foo1():
return 1
@reg('cmd2')
def foo2():
return 2
dispatcher()
面向对象方式实现:
使用setattr()和getattr()找到对象的属性(实际是在类上加的,实例找不到逐级往上找),比自己维护一个dict来建立名称和函数之间的关系要好;
实现1:
class Dispatcher:
def cmd1(self):
return 1
def reg(self,cmd,fn):
if isinstance(cmd,str):
setattr(self.__class__,cmd,fn) #放在类上最方便,self.__class__同type(self);不要在实例上定义,如果在实例上,setattr(self,cmd,fn),调用时要注意dis.reg('cmd2',lambda : 2)
else:
print('error')
def run(self):
while True:
cmd = input('plz input cmd: ')
if cmd.strip() == 'quit':
return
print(getattr(self,cmd.strip(),self.defaultfn)())
def defaultfn(self):
return 'default function'
dis = Dispatcher()
dis.reg('cmd2',lambda self: 2)
dis.run()
# print(dis.__class__.__dict__)
# print(dis.__dict__)
输出:
plz input cmd: cmd1
1
plz input cmd: cmd2
2
plz input cmd: cmd3
default function
plz input cmd: 11
default function
实现2:
class Dispatcher:
def __init__(self):
self._run()
def cmd1(self):
return 1
def cmd2(self):
return 2
def reg(self,cmd,fn):
if isinstance(cmd,str):
setattr(self.__class__,cmd,fn)
else:
print('error')
def _run(self):
while True:
cmd = input('plz input cmd: ')
if cmd.strip() == 'quit':
return
print(getattr(self,cmd.strip(),self.defaultfn)())
def defaultfn(self):
return 'default function'
Dispatcher()
输出:
plz input cmd: cmd1
1
plz input cmd: cmd2
2
plz input cmd: cmd3
default function
plz input cmd: abcd
default function
反射相关的魔术方法(__getattr__()、__setattr__()、__delattr__()):
__getattr__(),当在实例、实例的类及祖先类中查不到属性,才调用此方法;
__setattr__(),通过点访问属性,进行增加、修改都要调用此方法;
__delattr__(),当通过实例来删除属性时调用此方法,删自己有的属性;
一个类的属性会按照继承关系找,如果找不到,就是执行__getattr__(),如果没有这个方法,抛AttributeError;
查找属性顺序为:
instance.__dict__-->instance.__class__.__dict__-->继承的祖先类直到object的__dict__-->调用__getattr__(),如果没有__getattr__()则抛AttributeError异常;
__setattr__()和__delattr__(),只要有相应的操作(如初始化时self.x = x或运行时a.y = 200触发__setattr__(),del a.m则触发__delattr__())就会触发,做拦截用,拦截做增加或修改,属性要加到__dict__中,要自己完成;
这三个魔术方法的第一个参数为self,则如果用类名.属性操作时,则这三个魔术方法管不着;
例:
class A:
m = 6
def __init__(self,x):
self.x = x
def __getattr__(self, item): #对象的属性按搜索顺序逐级找,找到祖先类object上也没有对应属性,则最后找__getattr__(),如有定义__getattr__()返回该函数返回值,如果没有此方法,则报错AttributeError: 'A' object has no attribute 'y'
print('__getattr__',item)
print(A(10).x)
print(A(8).y)
输出:
10
__getattr__ y
None
例:
class A:
m = 6
def __init__(self,x):
self.x = x #__init__()中的self.x = x也调用__setattr__()
def __getattr__(self, item):
print('__getattr__',item)
# self.__dict__[item] = 'default_value'
def __setattr__(self, key, value):
print('__setattr__',key,value)
# self.__dict__[key] = value
def __delattr__(self, item):
print('__delattr__')
a = A(8) #初始化时的self.x = x也调用__setattr__()
a.x #调用__getattr__()
a.x = 100 #调用__setattr__(),实例的__dict__为空没有x属性,虽有触发__setattr__(),但没写到__dict__中,要自己写
a.x
a.y
a.y = 200
a.y
print(a.__dict__) #__getattr__()和__setattr__()都有,实例的__dict__为空,用a.x访问属性时按顺序都没找到最终调用__getattr__()
print(A.__dict__)
del a.m
输出:
__setattr__ x 8
__getattr__ x
__setattr__ x 100
__getattr__ x
__getattr__ y
__setattr__ y 200
__getattr__ y
{}
{'__module__': '__main__', 'm': 6, '__init__':
__delattr__
反射相关的魔术方法(__getattribute__):
__getattribute__(),实例所有属性调用,都从这个方法开始;
实例的所有属性访问,第一个都会调用__getattribute__()方法,它阻止了属性的查找,该方法应该返回(计算后的)值或抛AttributeError,它的return值将作为属性查找的结果,如果抛AttributeError则直接调用__getattr__(),表示属性没有找到;
为了避免在该方法中无限的递归,它的实现应该永远调用基类的同名方法以访问需要的任何属性,如object.__getattribute__(self,name);
除非明确知道__getattribute__()方法用来做什么,否则不要使用它,拦截面太大;
属性查找顺序:
实例调用__getattribute__()-->instance.__dict__-->instance.__class__.__dict__-->继承的祖先类直到object的__dict__-->调用__getattr__();
例:
class A:
m = 6
def __init__(self,x):
self.x = x
def __getattr__(self, item):
print('__getattr__',item)
# self.__dict__[item] = 'default_value'
def __setattr__(self, key, value):
print('__setattr__',key,value)
# self.__dict__[key] = value
def __delattr__(self, item):
print('__delattr__')
def __getattribute__(self, item):
print('__getattribute__',item)
raise AttributeError(item) #如果抛AttributeError则直接调用__getattr__(),表示属性没找到
# return self.__dict__[item] #递归调用
# return object.__getattribute__(self,item) #继续向后找,这样做没意义
a = A(8)
a.x
a.y
a.z
输出:
__setattr__ x 8
__getattribute__ x
__getattr__ x
__getattribute__ y
__getattr__ y
__getattribute__ z
__getattr__ z