在做程序开发中,我们常常会遇到这样的需求:需要执行对象里的某个方法,或需要调用对象中的某个变量,但是由于种种原因我们无法确定这个方法或变量是否存在,这是我们需要用一个特殊的方法或机制要访问和操作这个未知的方法或变量,这中机制就称之为反射
反射:用字符串的方式去访问对象的属性,调用对象的方法,python中一切皆对象,都可以使用反射。
- hasattr(obj, name):检查 obj 对象是否包含名为 name 的属性或方法。
- getattr(object, name[, default]):获取 object 对象中名为 name 的属性的属性值。
- setattr(obj, name, value,/):将obj 对象的 name 属性设为 value。
hasattr
判断一个对象是否有name属性或者name方法。有就返回True,没有就返回False
class Person(object):
def __init__(self,name):
self.name = name
def talk(self):
print("%s正在交谈"%self.name)
p = Person("santa")
print(hasattr(p,"talk")) # True。因为存在talk方法
print(hasattr(p,"name")) # True。因为存在name变量
print(hasattr(p,"abc")) # False。因为不存在abc方法或变量
getattr
获取对象的属性或者方法,如果存在则打印出来。hasattr
和getattr
配套使用
class Person(object):
def __init__(self,name):
self.name = name
def talk(self):
print("%s正在交谈"%self.name)
p = Person("santa")
n = getattr(p,"name") # 获取name变量的内存地址
print(n) # 此时打印的是:laowang
f = getattr(p,"talk") # 获取talk方法的内存地址
f() # 调用talk方法
# 我们发现getattr有三个参数,那么第三个参数是做什么用的呢?
s = getattr(p,"abc","not find")
print(s)
#若属性不存在,则返回默认值(第三个参数)
打印结果:
santa
santa正在交谈
not find
setattr
给对象的属性赋值,若属性不存在,先创建后赋值
def abc(self):
print("%s正在交谈"%self.name)
class Person(object):
def __init__(self,name):
self.name = name
p = Person("santa")
setattr(p,"talk",abc) # 将abc函数添加到对象中p中,并命名为talk
p.talk(p) # 调用talk方法,因为这是额外添加的方法,需手动传入对象
#结果
#santa正在交谈
setattr(p,"age",30) # 添加一个变量age,赋值为30
print(p.age) # 打印结果:30
delattr
删除该对象指定的一个属性。注意:不能用于删除方法
class Person(object):
def __init__(self,name):
self.name = name
def talk(self):
print("%s正在交谈"%self.name)
p = Person("laowang")
delattr(p,"name") # 删除name变量
print(hasattr(p,'name')) #False
反射的应用
# 反射应用:
根据用户输入进行动态调用
class FileControl:
def run(self):
while True:
# 让用户输入上传或下载功能的命令:
user_input = input('请输入 上传(upload) 或 下载(download) 功能:').strip()
# 通过用户输入的字符串判断方法是否存在,然后调用相应的方法
if hasattr(self, user_input):
func = getattr(self, user_input)
func()
else:
print('输入有误!')
def upload(self):
print('文件正在上传...')
def download(self):
print('文件正在下载...')
file_control_obj = FileControl()
file_control_obj.run()
这就是python的反射,它的核心本质其实就是利用字符串的形式去对象(模块)中操作(查找/获取/删除/添加)成员,一种基于字符串的事件驱动!
_import_(了解)
要是想导入不同目录下的函数,可以这样:动态导入模块
def run():
inp = input("请输入您想访问页面的url: ").strip()
modles, func = inp.split("/")
obj = __import__("lib." + modules, fromlist=True) # 注意fromlist参数 modles是url
if hasattr(obj, func):
func = getattr(obj, func)
func()
else:
print("404")
if __name__ == '__main__':
run()
思考:
可能有人会问python不是有两个内置函数exec和eval吗?他们同样能够执行字符串。比如:
exec("print('haha')")
结果:
haha
那么直接使用它们不行吗?非要那么费劲地使用getattr,__import__干嘛?
其实,在上面的例子中,围绕的核心主题是如何利用字符串驱动不同的事件,比如导入模块、调用函数等等,这些都是python的反射机制,是一种编程方法、设计模式的体现,凝聚了高内聚、松耦合的编程思想,不能简单的用执行字符串来代替。当然,exec和eval也有它的舞台,在web框架里也经常被使用。