Python反射机制用沛齐老师总结的话说就是:利用字符串的形式去对象(模块)中操作(寻找)成员。
- getattr(object, name)
object代表模块,name代表模块中的属性或成员,该函数表达获取object模块中的
属性或者成员。例如getattr(commons,"login")表示去commons模块里去找
login成员,而这里的login在commons模块里是一个函数名,代表着login的函数体。
- hasattr(object,name)
object代表模块,name代表模块中的属性或成员,该函数用于确认object模块中是否存在
name属性或成员,返回只为true或false,可搭配getattr(object,name)方法一起使用。
- setattr(x, y, v)
设置某个模块里的成员或属性,setattr(x, 'y', v) is equivalent to ``x.y = v''
- delattr(x, y)
删除某个模块里的成员或属性,delattr(x, 'y') is equivalent to ``del x.y''
- __import__("模块名")
与普通的import不同的是,__import__()括号里面导入的字符串格式的模块名。
练习一 : 最容易理解的网站资源调用方式:
import commons def run(): inp = input('请输入要访问的URL:') if inp == 'login': commons.login() elif inp == 'logout': commons.logout() elif inp == 'home': commons.home() else: print('404')
if __name__ == '__main__': # 通过主函数进行调用
run()
# 当用户想要访问login页面的时候,直接模拟输入login字符串, # 调用导入的commons.login()函数即可打印输出登录页面, # 调用导入的commons.logout()即可打印退出页面 # 依此类推... # 但是如果一个网站有成千上万的网页的时候要写成千上万个if-elif-else吗, # 效率太低了,这个时候就用到了下面的Python的反射。
练习二 : 借助python反射(hasattr()和getattr()方法)来调用网站资源
import commons def run(): inp = input('请输入要访问的URL:') if hasattr(commons,inp): func = getattr(commons, inp) func() else: print('404-页面不存在')
if __name__ == '__main__': # 通过主函数进行调用
run()
# 该实例利用了Python反射,inp为接收用户输入字符串,类型为字符串类型 # func代表了commons模块中的login函数名。因此以上语句表达了这样的含义: # 接收用户输入的一个字符串并赋值给变量inp,通过hasattr函数确认commons模块中 # 是否存在inp属性,如果有的话继续通过getattr()调用该函数并打印,如果没有就打印404 # 我们在浏览器上访问网站上具体某个网页的时候,URL的通常是 # 通过gehttp://www.example.com/account/login这样的结构向服务器发送get或post请求。 # 但是网站上有成千上万个功能模块或网页,而如果要向通过这种结构的URL请求资源的话, # 就需要将网站目录结构优化一下了。如: # 目录结构优化: # 与用户账户相关的模块都去account.py中查找,与后台管理的都去manager.py中去查找 # --根目录 # --index.py # --account.py # --login() # --logout() # --manager.py # --order() # 要想通过account/login这种方式请求某个资源的时候就必须借助__import__("模块名")来导入模块了。
练习三 : 使用__import__()导入模块,并使用split分割模块与属性,使python反射更灵活
# __import__() 括号内应该是字符串,通过输入字符串形式的模块名就可以导入该模块了。 # obj = __import__("commons") # obj.login() 等同与import commons , commons.login() def run(): # 以输入account/login为例(account模块中的login函数) inp = input('请输入要访问的URL:') # 通过split将输入inp分割成account模块和login函数,account赋值给m,login赋值给f m , f = inp.split('/') # 将m以字符串格式导入,并指向obj obj = __import__(m) # 获取到了模块与模块内的属性或函数,就可以继续借助反射来进行操作了。 if hasattr(obj,f): func = getattr(obj,f) func() else: print('404') # 输出结果: # 请输入要访问的URL:account/login # 登陆页面
if __name__ == '__main__': # 通过主函数进行调用
run()
练习四 : 导入模块不在当前路径,需要用字符串拼接成完整模块路径,并指定fromlist属性。
# 以上练习时的目录结构基本上都是在同一目录结构里的,如果需要导入的模块在某个文件夹中的时候,在使用 # __import__()导入模块的时候就需要考虑目录结构然后用字符串的拼接成完整的模块路径了,例如以下目录结构。 # 要导入的account放在了lib文件夹下。 # --根目录 # --index.py # --lib(文件夹) # --account.py # --login() # --logout() # --manager.py # --order() def run(): inp = input('请输入要访问的URL:') m , f = inp.split('/') # 要导入模块的完整路径:lib.account # 注意:拼接后的模块路径为"lib.account",但是默认情况下python只会导入点(.)前的第一个模块,也就是lib # 需要在括号后面加一个fromlist = True属性才可以导入完整的模块路径。 obj = __import__("lib." + m , fromlist=True) if hasattr(obj,f): func = getattr(obj,f) func() else: print('404')
if __name__ == '__main__': # 通过主函数进行调用
run()
# 输出结果: # 请输入要访问的URL:account/login # 登陆页面
commons.py
def login(): print('登陆页面') def logout(): print('退出页面') def home(): print('主页面')
account.py
def login(): print('登陆页面') def logout(): print('退出页面')