Python是一门动态强类型语言,想要实现为类增添属性有好几种方式,本篇博客就此做一个简单的总结。
在总结之前,让我们先来看看python的反射
运行时,区别于编译时,指的是程序被加载到内存中执行的时候。 反射,reflection,指的是运行时获取类型定义信息。 一个对象能够在运行时,像照镜子一样,反射出其类型信息。
简单说,在Python中,能够通过一个对象,找出其type、class、attribute 或 method的能力,称为反射或者自省。
具有反射能力的函数有 type()、isinstance()、calladle()、getattr()等
需求: 有一个Point类,需要查看它实例的属性,并修改它。动态为实例添加属性 思考:
首先我们要访问到这个实例的属性,可以通过实例的字典,或者成员操作符来进行访问第二步修改,因为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都是定义时就决定了,因此反射能力具有更大的灵活性。