反射 & 命令分发器
运行时,区别于编译时,指的是程序被加载到内存中执行的时候
反射,reflection,指的是运行时获取类型定义的信息
一个对象能够在运行时,像找镜子一样,反射处其类型信息
在Python中,能够通过一个对象,找出其type,class,attribute,method的能力,称为反射或自省
具有反射能力的函数: type() isinstance() callable() dir() getattr()
def dispatcher():
cmds={}
def reg(cmd,fn):
if isinstance(cmd,(str,)):
cmds.setdefault(cmd,fn)
else:
raise TypeError('TypeError')
print(cmds)
def run():
while True:
cmd=input('cmd: ').strip()
if cmd.lower() == 'q':
return
print(cmds.get(cmd,default)())
def default():
print('default')
return reg,run
reg,run=dispatcher()
reg('b1',lambda :print(11111111))
reg('b2',lambda :222)
print(run())
class Dispatcher:
def cmd1(self):
return 'cmd1'
def reg(self,cmd,fn):
if isinstance(cmd,(str,)):
# setattr(self.__class__,cmd,fn)
setattr(type(self),cmd,fn)
else:
raise TypeError
def run(self):
while True:
cmd=input('commmand: ').strip()
if cmd.upper() == 'Q':
return
print(getattr(self,cmd,self.default)())
def default(self):
return 'default'
b=Dispatcher()
b.reg('cmd2',lambda self:22222222)
b.reg('cmd3',lambda self:333333333333)
b.run()
class Dispatcher:
def __init__(self):
print('aaaaaaaaaa')
self._run() # 初始化时就调用实例的_run方法(类的)
print(self.__dict__)
print('bbbbbbbbbbbbbbbb')
# self.b
def cmd1(self):
return 'cmd1'
def cmd2(self):
return 'cmd2'
def _run(self):
while True:
cmd=input('cmd: ').strip()
if cmd.lower() == 'q':
return
print(getattr(self,cmd,lambda : 'default')())
Dispatcher()
# print(b.__dict__)
反射相关的函数和方法
一个Point类,查看它的实例属性,动态增加其属性
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('show:{{{}:{}}}'.format(self.x,self.y))
p=Point(22,33)
print(str(p))
print(p.__dict__)
p.__dict__['y']=44
print(p.__dict__)
p.z=10
print(p.__dict__)
print(dir(p),type(dir(p)))
print(p.__dir__())
上面的例子,访问方式不优雅,Python提供了内置函数
内建函数 | 意义 |
getattr(object,name[,default]) | 通过name访问object属性,属性不存在,返回default,未设置default,抛出AttributeError,name必须为字符串 |
setattr(object,name,value) | object属性存在,覆盖,不存在,新增 |
hasattr(object,name) | 判断对象是否有name的属性,name必须为str |
用上面方法重写代码
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)
p1=Point(11,22)
p2=Point(33,44)
print(repr(p1),repr(p2),sep='\n')
print(p1.__dict__)
setattr(p1,'y',999)
setattr(p1,'z',888)
print(getattr(p1,'__dict__'))
print(p1.__dict__)
# 动态调用方法
if hasattr(p1,'show'):
p1.show()
if hasattr(p1,'uiop'):
p1.uiop()
#动态增加方法
# 为类增加方法
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))
# 为实例增加方法,未绑定
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) # 未绑定需手动传参self
print(p1)
def fn(self,other):
self.x=self.x-other.x
self.y=self.y-other.y
return self
if hasattr(p1,'sub'):
setattr(p1,'sub',lambda self,other:exec('self.x=self.x-other.x;self.y=self.y-other.y'))
# setattr(p1,'sub',fn)
print(p1.sub)
print(p1.sub(p1,p1))
print(p1)
print(p1.__dict__)
print(Point.__dict__)
反射相关的魔术方法
__getattr__ __setattr__ __delattr__
__getattr__:
class Base:
n=00
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 '{} object missing {}'.format(self.__class__.__name__,item)
p1=Point(11,22)
print(p1.x)
print(p1.z)
print(p1.n)
# print(p1.mm)
print(getattr(p1,'uio','uioopp'))
# 使用getattr()时,如果类存在__getattr__魔术方法,可不设置default,如果未设置default,也不存在__getattr__则会抛异常
# 直接访问属性时,没有__getattr__直接抛异常
属性会按照继承关系找,找不到,执行__getattr__方法,如果没有这个方法,抛出AttributeError异常,表示找不到该属性
查找属性顺序为:
instance.__dict__ => instance.__class__.__dict__ => 继承的祖先类(until object).__dict__ => 调用getattr()
__setattr__
class Base:
n=00
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 '{} object missing {}'.format(self.__class__.__name__,item)
def __setattr__(self,key,value):
print('setattr {}={}'.format(key,value)) # 覆盖 object的方法后,属性未写到dict中
p1=Point(11,22)
print(p1.x)
print(p1.z)
print(p1.n)
print(p1.mm)
p1.x=55
print(p1.__dict__)
p1.__dict__['x']=66 # 直接操作实例自身的属性__dic__不会调用__setattr__
print(p1.__dict__)
print(p1.x)
setattr(p1,'vv',888)
print(p1.__dict__)
实例通过 "." 或setattr设置属性,like self.x='xxxxxxx' or setattr(p1,'x','xxxxxxxxxxxx'),就会调用__setattr__()魔术方法,但是直接操作自身的字典,like p1.__dict__['x']='xxxxxxxxxxxxx'
则不会触发__setattr__的执行
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 '{} object missing {}'.format(self.__class__.__name__,item)
def __setattr__(self,key,value):
print('setattr {}={}'.format(key,value)) # 覆盖 object的方法后,属性未写到dict中
self.__dict__[key]=value # 执行写入操作
__setattr__方法,可以拦截对实例属性的增加,修改,如果要设置生效,需要手动操作实例的__dict__
__delattr__
class Ore:
z=55
def __init__(self,x,y):
self.x=x
self.y=y
def __delattr__(self,item): # 实例方法
print('Can not del {}'.format(item))
p=Ore(11,22)
del p.x
p.z=33
del p.z
print(Ore.__dict__)
print(p.__dict__)
del Ore.z
print(Ore.__dict__)
print(p.__dict__)
可以阻止通过实例删除属性的操作,但是通过类依然可以删除属性,因为这三个魔术方法都是针对实例
__getattribute__
class Base:n=00
class Ore(Base):
z=66
def __init__(self,x,y):
self.x=x
self.y=y
def __getattr__(self,item):
return '{} object missing {}'.format(self.__class__.__name__,item)
def __getattribute__(self,item):
return item
o=Ore(11,22)
print(o.__dict__)
print(o.x)
print(o.z)
print(o.mmmm)
print(Ore.__dict__)
print(Ore.z)
实例的所有属性访问,第一个都会调用__getattribute__方法,__getattribute__阻止了实例属性的查找,该方法返回计算后的值或者抛出一个AttributeError异常
它的return值将作为属性查找的结果,如果抛出AttributeError异常,则会直接调用__getattr__方法,表示属性没有找到
class Base:n=00
class Ore(Base):
z=66
def __init__(self,x,y):
self.x=x
self.y=y
def __getattr__(self,item):
return '{} object missing {}'.format(self.__class__.__name__,item)
def __getattribute__(self,item):
raise AttributeError('Attribute Not Found')
return item
o=Ore(11,22)
print(o.__dict__)
上图产生循环调用
修改代码
class Base:n=00
class Ore(Base):
z=66
def __init__(self,x,y):
self.x=x
self.y=y
def __getattr__(self,item):
return '{} object missing {}'.format(self,item)
def __getattribute__(self,item):
raise AttributeError('Attribute Not Found')
return item
o=Ore(11,22)
print(o.__dict__)
print(o.x)
print(o.z)
print(o.mmmm)
print(Ore.__dict__)
print(Ore.z)
所有的实例属性访问,先调用__getattribute__后直接raise AttributeError后调用了__getattr__方法
class Base:n=00
class Ore(Base):
z=66
def __init__(self,x,y):
self.x=x
self.y=y
def __getattr__(self,item):
return '{} object missing {}'.format(self,item)
def __getattribute__(self,item):
# raise AttributeError('Attribute Not Found')
# return self.__dict__[item] # 会产生循环递归
return object.__getattribute__(self,item) # 正确的返回方法
o=Ore(11,22)
print(o.__dict__)
print(o.x)
print(o.z)
print(o.mmmm)
print(Ore.__dict__)
print(Ore.z)
__getattribute__方法中,为了避免在该方法中无限递归,它的实现应为永远调用基类的同名方法以访问需要的任何属性,例如 object.getattribute(self,name)
魔术方法 | 意义 |
__getattr__ | 通过搜索实例,实例的类,以及祖先类查不到该属性时调用 |
__setattr__ | 通过 "." 或 setattr(object,key,value)访问实例属性,进行增加,修改时调用 |
__delattr__ | 通过实例删除属性时调用 |
__getattribute__ | 实例的所有属性调用都从此方法起始 |
属性查找顺序:
__getattribute__() => instance.__dict__ => instance.__class__.__dict__ => 祖先类(until object).__dict__ => __getattr__()
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律