Python-反射

本文主要介绍Python中的反射机制,反射指运行时获取类型定义信息,通过对象找出其type、class、attribute或method。文中列举了具有反射能力的函数,探讨动态增删属性与装饰器、Mixin的差异,还介绍反射相关魔术方法及属性查找顺序。

概述

运行时,区别于编译时,指的是程序被加载到内存中执行的时候
反射,reflection,指的是运行时获取类型定义信息
一个对象能够在运行时,像照镜子一样,反射处其类型信息
简单说,在python中,能够通过一个对象,找出其type、class、attribute或method的能力,称为反射或者自省
具有反射能力的函数有type()、isinstance()、callable()、dir()、getattr()等
反射相关的函数和方法
需求
有一个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(self.x, self.y)

p = Point(4, 5)
print(p) # Point(4, 5)
print(p.__dict__) # {'x': 4, 'y': 5}
p.__dict__['y'] = 16 # {'x': 4, 'y': 16}
print(p.__dict__) # {'x': 4, 'y': 16, 'z': 10}
p.z = 10
print(p.__dict__)
print(dir(p))
print(p.__dir__())

上例通过属性字典__dict__来访问对象的属性,本质上也是利用的反射的能力。
Python提供了内置的函数

内建函数意义
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 "Point({}, {})".format(self.x, self.y)

    def show(self):
        print(self)

p1 = Point(4, 5)
p2 = Point(10, 10)
print(repr(p1), repr(p2))
print(p1.__dict__) # {'x': 4, 'y': 5}
setattr(p1, 'y', 16)
setattr(p1, 'z', 16)
print(getattr(p1, '__dict__')) # {'x': 4, 'y': 16, 'z': 16}

# 动态调用方法
if hasattr(p1, 'show'):
    getattr(p1, 'show')() # Point(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) # <function <lambda> at 0x000002018E6E51E0>
print(p1.add) # <bound method <lambda> of <__main__.Point object at 0x000001EC6C2A86A0>>
print(p1.add(p2)) # 绑定 # 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)) # Point(0, 0)
print(p1.sub) # <function <lambda> at 0x0000018C324E5620>

# add在谁里面, sub在谁里面
print(p1.__dict__) # {'x': 4, 'y': 16, 'z': 16, 'sub': <function <lambda> at 0x00000180472D5620>}
print(Point.__dict__)
# {'__module__': '__main__', '__init__': <function Point.__init__ at 0x00000180472D5158>,\
# '__str__': <function Point.__str__ at 0x00000180472D5268>, 'show': <function Point.show at 0x00000180472D5598>, \
# '__dict__': <attribute '__dict__' of 'Point' objects>, '__weakref__': <attribute '__weakref__' of 'Point' objects>,\
# '__doc__': None, 'add': <function <lambda> at 0x00000180472D51E0>}

思考

  • 这种动态增加属性的方式和装饰器修饰一个类、Mixin方式的差异在哪里

这种动态增删属性的方式是运行时改变类或者实例的方式,但是装饰器或Mixin都是定义时就决定了,因此反射能力具有更大的灵活性

练习

  • 命令分发器,通过名称找到对应的函数执行

思路:名称找对象的方法

class Dispatcher:
    def __init__(self):
        pass

    def reg(self, name, fn):
        setattr(self, name, fn)

    def run(self):
        while True:
            cmd = input('>>>').strip()
            if cmd == 'quit':
                break

            getattr(self, cmd, lambda :print('Unknowm Cmd {}'.format(cmd)))()

dis = Dispatcher()
dis.reg('ls', lambda  :print('ls'))

dis.run()

上例中使用getattr方法找到对象的属性的方式,比自己维护一个字典来建立名词和函数之间的关系的方式好多了

反射相关的魔术方法

__getattr__()__setattrr__()__delattr__()

__getattr__()

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) # 4
print(p1.z) # 6
print(p1.n) # 0
print(p1.y) # 5

实例属性会按照继承关系找,如果找不到,就会执行__getattr__()方法,如果没有这个方法,就会抛出AttrbuteError异常表示找不到属性

  • 查找属性的顺序:
    instance.__dict__ --> instance.__class__.__dict__ --> 继承的祖先类(直到object)的__dict --> 找不到调用__getattr__()

__setattrr__()

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))

p1 = Point(4,5)
print(1, p1.x)
print(2, p1.z) 
print(3, p1.n)
print(4, p1.t)
p1.x = 50
print(5, p1.x)
print(6, p1.__dict__)
p1.__dict__['x'] = 60
print(7, p1.__dict__)
print(8, p1.x)

执行结果
setattr x=4
setattr y=5
1 missing x
2 6
3 0
4 missing t
setattr x=50
5 missing x
6 {}
7 {'x': 60}
8 60

实例通过.点号设置属性,例如self.x = x属性赋值,就会调用__setattr_-(),属性要加到实例的__dict__中,就需要自己完成

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 # 操作字典

__setattr__()方法,可以拦截对实例属性的增加、修改操作,如果要设置生效,需要自己操作实例的__dict__

例子

__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('setattr key ', key)
        print('setattr key ', 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)

# 执行结果
setattr key  x
setattr key  4
setattr key  y
setattr key  5
{'a': 5}
{'__module__': '__main__', 'z': 100, 'd': {'x': 4, 'y': 5},\
 '__init__': <function A.__init__ at 0x000001062E6C5158>,\
  '__getattr__': <function A.__getattr__ at 0x000001062E6C5268>, \
  '__setattr__': <function A.__setattr__ at 0x000001062E6C5598>, \
  '__delattr__': <function A.__delattr__ at 0x000001062E6C5620>, \
  '__doc__': None}
------ x
------ y
4 5
5

__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 = 15
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 0x0000029FBF8B5158>, \
'__delattr__': <function Point.__delattr__ at 0x0000029FBF8B5268>, '__dict__': <attribute '__dict__' of 'Point' objects>, \
'__weakref__': <attribute '__weakref__' of 'Point' objects>, '__doc__': None}
{'x': 4, 'y': 5, 'z': 15}
{'__module__': '__main__', '__init__': <function Point.__init__ at 0x0000029FBF8B5158>, \
'__delattr__': <function Point.__delattr__ at 0x0000029FBF8B5268>, '__dict__': <attribute '__dict__' of 'Point' objects>,\
 '__weakref__': <attribute '__weakref__' of 'Point' objects>, '__doc__': None}

可以阻止通过实例来删除属性的操作。但是通过类依然可以删除属性

__getattribute__()

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__) # __dict__
print(p1.x) # x
print(p1.z) # z
print(p1.n) # n
print(p1.t) # t
print(Point.__dict__)
# {'__module__': '__main__', 'z': 6, '__init__': <function Point.__init__ at 0x0000016930285158>,\
 '__getattr__': <function Point.__getattr__ at 0x0000016930285268>, \
 '__getattribute__': <function Point.__getattribute__ at 0x0000016930285598>, '__doc__': None}

print(Point.z) # 6

实例的所有的属性访问,第一个都会调用__getattribute__方法,它阻止了属性的查找,该方法应该返回(计算后的)值或者抛出一个AttributeError异常

  • 它的return值将作为属性查找的结果
  • 如果抛出AttributeError异常,则会直接调用__getattr__方法,表示属性没有找到
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):
        # raise AttributeError("Not Found")
        # pass
        # return self.__dict__[item] # RecursionError
        return object.__getattribute__(self, item)
p1 = Point(4, 5)
print(p1.__dict__) # {'x': 4, 'y': 5}
print(p1.x) # 4
print(p1.z) # 6
print(p1.n) # 0
print(p1.t) # missing t
print(Point.__dict__)
# {'__module__': '__main__', 'z': 6, '__init__': <function Point.__init__ at 0x0000029AF7765158>, \
'__getattr__': <function Point.__getattr__ at 0x0000029AF7765268>, \
'__getattribute__': <function Point.__getattribute__ at 0x0000029AF7765598>, '__doc__': None}

print(Point.z) # 6
  • __getattribute__方法中为了避免在该方法中无限的递归,它的实现应该永远调用基类的同名方法打以访问需要的任何属性,例如object.__getattribute__(self, item)
  • 除非明确地知道__getattribute__方法用来做什么,否则不要使用它

总结

魔术方法意义
__getattr__()当通过搜索实例、实例的类及祖先类查不到属性,就会调用此方法
__setattr__()通过.点访问实例属性,进行增加、修改都要调用它
__delattr__()当通过实例来删除属性时调用此方法
__getattribute__()实例所有的属性调用都从这个方法开始
  • 属性查找顺序:
    实例调用__getattribute__() --> instance.__dict__ --> instance.__class__.__dict__ --> 继承的祖先类(直到object)的__dict__–>调用__getattr__()
基于51单片机,实现对直流电机的调速、测速以及正反转控制。项目包含完整的仿真文件、源程序、原理图和PCB设计文件,适合学习和实践51单片机在电机控制方面的应用。 功能特点 调速控制:通过按键调整PWM占空比,实现电机的速度调节。 测速功能:采用霍尔传感器非接触式测速,实时显示电机转速。 正反转控制:通过按键切换电机的正转和反转状态。 LCD显示:使用LCD1602液晶显示屏,显示当前的转速和PWM占空比。 硬件组成 主控制器:STC89C51/52单片机(与AT89S51/52、AT89C51/52通用)。 测速传感器:霍尔传感器,用于非接触式测速。 显示模块:LCD1602液晶显示屏,显示转速和占空比。 电机驱动:采用双H桥电路,控制电机的正反转和调速。 软件设计 编程语言:C语言。 开发环境:Keil uVision。 仿真工具:Proteus。 使用说明 液晶屏显示: 第一行显示电机转速(单位:转/分)。 第二行显示PWM占空比(0~100%)。 按键功能: 1键:加速键,短按占空比加1,长按连续加。 2键:减速键,短按占空比减1,长按连续减。 3键:反转切换键,按下后电机反转。 4键:正转切换键,按下后电机正转。 5键:开始暂停键,按一下开始,再按一下暂停。 注意事项 磁铁和霍尔元件的距离应保持在2mm左右,过近可能会在电机转动时碰到霍尔元件,过远则可能导致霍尔元件无法检测到磁铁。 资源文件 仿真文件:Proteus仿真文件,用于模拟电机控制系统的运行。 源程序:Keil uVision项目文件,包含完整的C语言源代码。 原理图:电路设计原理图,详细展示了各模块的连接方式。 PCB设计:PCB布局文件,可用于实际电路板的制作。
【四旋翼无人机】具备螺旋桨倾斜机构的全驱动四旋翼无人机:建模与控制研究(Matlab代码、Simulink仿真实现)内容概要:本文围绕具备螺旋桨倾斜机构的全驱动四旋翼无人机展开研究,重点进行了系统建模与控制策略的设计与仿真验证。通过引入螺旋桨倾斜机构,该无人机能够实现全向力矢量控制,从而具备更强的姿态调节能力和六自由度全驱动特性,克服传统四旋翼欠驱动限制。研究内容涵盖动力学建模、控制系统设计(如PID、MPC等)、Matlab/Simulink环境下的仿真验证,并可能涉及轨迹跟踪、抗干扰能力及稳定性分析,旨在提升无人机在复杂环境下的机动性与控制精度。; 适合人群:具备一定控制理论基础和Matlab/Simulink仿真能力的研究生、科研人员及从事无人机系统开发的工程师,尤其适合研究先进无人机控制算法的技术人员。; 使用场景及目标:①深入理解全驱动四旋翼无人机的动力学建模方法;②掌握基于Matlab/Simulink的无人机控制系统设计与仿真流程;③复现硕士论文级别的研究成果,为科研项目或学术论文提供技术支持与参考。; 阅读建议:建议结合提供的Matlab代码与Simulink模型进行实践操作,重点关注建模推导过程与控制器参数调优,同时可扩展研究不同控制算法的性能对比,以深化对全驱动系统控制机制的理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值