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

前言:

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都是定义时就决定了,因此反射能力具有更大的灵活性。

你可能感兴趣的:(Python--动态为类增加属性的几种方式总结)