Python:类魔术方法、反射、描述器

本文详细介绍了Python中的反射和描述器。反射是指程序在运行时获取类定义信息的能力,包括type()、isinstance()等函数。描述器是一种特殊类型的数据类型,具有__get__、__set__等方法,用于控制属性的访问。文章通过实例解析了描述器的工作原理及其实现,展示了描述器在Python中的广泛应用,如方法、静态方法、类方法和property。
摘要由CSDN通过智能技术生成

目录

反射

反射相关函数和方法

练习

反射相关的模式魔术方法

反射总结:

描述器

描述器的表现

描述器定义

属性的访问顺序

本质(进阶)

Python中的描述器

 


​​​​​​​

反射

概述:

  • 运行时,区别于编译时,指的是程序被加载到内存中执行的时候
  • 反射【reflection】,指的是运行时获取类定义的信息
  • 一个对象能够在运行时,像照镜子一样,反射出其类型信息
  • 简单说,在Python中,能够通过一个对象,找出其type、class、attribute或method的能力,称其反射或者自省
  • 具有反射能力的函数有:type()、isinstance()、callable()、dir()、getattr()

反射相关函数和方法

需求:有个Poine类,查看它实例的属性,并修改它。动态为实例增加属性

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(sorted(dir(p)))   #  ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'show', 'x', 'y', 'z']
print(sorted(p.__dir__()))  # ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'show', 'x', 'y', 'z']

上例通过属性字典【__dict__】来访问对象的属性,本质上也是利用的反射的能力,但是上面的例子中,访问的方式不优雅,Python提供了内置的函数
 

内建函数 意义
getattr(object,name[,default]) 通过name返回object的属性值。当属性不存在,将使用default返回,如果没有default,则抛出AttributeErrot。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.x,self.y)

a = Point(4,5)
b = Point(10,10)
print(repr(a),repr(b))  #   <__main__.Point object at 0x017B9250> <__main__.Point object at 0x017B9CD0>
print(a.__dict__)   #   {'x': 4, 'y': 5}
setattr(a,"y",16)
setattr(a,"z",100)
print(getattr(a,"__dict__"))    #   {'x': 4, 'y': 16, 'z': 100}

# 动态调用方法
if hasattr(a,"show"):
    getattr(a,"show")() #   4 16

#    动态增加方法
#    为类增加方法
if not hasattr(Point,"add"):
    setattr(Point,"add",lambda self,other:Point(self.x+other.x,self.y+self.y))

print(Point.add)    #   <function <lambda> at 0x00BE17C8>
print(a.add)       #   <bound method <lambda> of <__main__.Point object at 0x00C191D0>>
print(a.add(b))     #   Point(14,32)

#为实例增加方法,为绑定
if not hasattr(a,"sub"):
    setattr(a,"sub",lambda self,other:Point(self.x-other.x,self.y-self.y))

print(a.sub(a,a))   #   Point(0,0)
print(a.sub)        #   <function <lambda> at 0x01746468>
print(a.sub())      #   会报错,不会自动把self,传入到第一个参数里面
#   add在谁里面,sub在谁里面
print(a.__dict__)   #   {'x': 4, 'y': 16, 'z': 100, 'sub': <function <lambda> at 0x02356468>}
print(Point.__dict__)   #   {'__module__': '__main__', '__init__': <function Point.__init__ at 0x0230C468>, '__str__': <function Point.__str__ at 0x02356150>, 'show': <function Point.show at 0x023564B0>, '__dict__': <attribute '__dict__' of 'Point' objects>, '__weakref__': <attribute '__weakref__' of 'Point' objects>, '__doc__': None, 'add': <function <lambda> at 0x0230C228>}

思考:这种增加属性的方式和装饰器一个类、Mixin方式差异?

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

练习

  • 命令分发器,通过名称找对应的函数来执行【思路:名称找对象的方法】

class Dispathcer:

    def cmd1(self):
        print("cmd1")

    def reg(self,cmd,fn):
        if isinstance(cmd,str):
            setattr(self.__class__,cmd,fn)
        else:
            print("error")

    def run(self):
        while True:
            cmd = input("请输入命令:").strip()
            if cmd.strip() == "quit":
                return
            getattr(self,cmd,self.defaultfn)()

    def defaultfn(self):
        print("default")

a = Dispathcer()
# a.run()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值