前言:
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都是定义时就决定了,因此反射能力具有更大的灵活性。
本文围绕Python反射展开,介绍反射指运行时获取类型定义信息,即通过对象找出其type、class、attribute或method的能力。还给出具有反射能力的函数,如type()等。通过Point类实例,展示利用反射动态增删属性,对比其与装饰器、Mixin方式,体现反射灵活性。
5277

被折叠的 条评论
为什么被折叠?



