Python--动态为类增加属性的几种方式总结

本文围绕Python反射展开,介绍反射指运行时获取类型定义信息,即通过对象找出其type、class、attribute或method的能力。还给出具有反射能力的函数,如type()等。通过Point类实例,展示利用反射动态增删属性,对比其与装饰器、Mixin方式,体现反射灵活性。
部署运行你感兴趣的模型镜像

前言:

Python是一门动态强类型语言,想要实现为类增添属性有好几种方式,本篇博客就此做一个简单的总结。


在总结之前,让我们先来看看python的反射


反射概述:

运行时,区别于编译时,指的是程序被加载到内存中执行的时候。
反射,reflection,指的是运行时获取类型定义信息。
一个对象能够在运行时,像照镜子一样,反射出其类型信息。


简单说,在Python中,能够通过一个对象,找出其type、class、attribute 或 method的能力,称为反射或者自省。


具有反射能力的函数有 type()、isinstance()、calladle()、getattr()等

反射相关的函数及方法:

需求:
有一个Point类,需要查看它实例的属性,并修改它。动态为实例添加属性
思考

  1. 首先我们要访问到这个实例的属性,可以通过实例的字典,或者成员操作符来进行访问
  2. 第二步修改,因为Python是动态强类型语言,所以我们只需要直接进行赋值即可进行修改

代码:

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(3, 4)
print(p1)
print(p1.__dict__)
p1.__dict__['y'] = 10
print(p1.__dict__)
p1.z = 20
print(p1.__dict__)
print(dir(p1))
print(p1.__dir__())

总结:
基本功能是实现了,通过属性字典__dict__来访问对象的属性,本质上也是利用的反射的能力。但是我们发现这样的方式不够优雅,Python为我们提供了内置的函数。如下所示:

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

    def show(self):
        return self

    __repr__ = __str__


p1 = Point(4, 5)
p2 = Point(3, 2)

print(repr(p1), repr(p2), sep='\n')
print(p1.__dict__)
setattr(p1, 'x', 5)
setattr(p1, 'u', 5)
print(getattr(p1, '__dict__'))

if hasattr(p1, 'x'):
    print('Yes, i have')
    print(getattr(p1, 'xx', 2000))

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)
print(p1.sub(p1, p2))
print(p1.__dict__)
print(Point.__dict__)

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

getattr()/setattr()Mixin装饰器修饰类
优点1.通过反射来动态为类增加属性,最为灵活1.使用多继承的方式来实现对类的功能增加
2.符合OCP原则,多继承、少修改
3.从设计模式的角度来说,多组合,少继承
1.简单方便,在需要的地方动态增加,直接使用装饰器
2.可以为类灵活的增加功能
局限性虽然动态为类增加了属性,但是同时也修改了类的内容不会修改原有类属性,但是灵活性比前者低可以通过先继承后修饰的方式,来避免对原有第三方库的修改
增加时机在运行时改变类或实例在类定义时,编译前在类定义时,编译前

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

您可能感兴趣的与本文相关的镜像

Python3.10

Python3.10

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值