在python反射与自省getattr入门篇中理清了getattr中传入的两个参数 对象(object)与 字符串(name)的概念,本篇就结合一些实际案例说说getattr的应有场景。
一、一个简单的应用
1、if调用
在实际应用,经常会遇到定义N个函数,需要根据用户的输入的内容来决定要执行的函数。这里以两个函数为例:
#!/usr/bin/env python # coding=utf-8 ## author: yangbk(itybku@139.com) ## site: www.361way.com def funa(): print 'running funa ......' def funb(): print 'running funb ......' action = raw_input('请输入地址:') if action == "funa": funa() elif action == "funb": funb()
如上面的示例的写法,当有N个循环时,需要我们通过if写N个循环。这是很蛋疼的事。能不能简化下写法呢?接着看如何优化。
2、字典map映射
代码如下:
#!/usr/bin/env python # coding=utf-8 ## author: yangbk(itybku@139.com) ## site: www.361way.com def funa(): print 'processNavigate' def funb(): print 'processCreateTab' action = raw_input('请输入地址:') actionMap = {"processNavigate":funa ,"processCreateTab":funb} actionMap[action]()
便于理解dict中哪一个是字符串,那一个是函数,我这里稍微修改了下代码。当输入processNavigate时执行函数funa,输入processCreateTab时执行funb。这个不难理解,action为输入的字符串,actionMap[action]得到的是函数名,函数执行时需要在其后加括号。
这个写法较第一种写法固然要好,不过还有一个不好的地方,就是需要维护dict字典,每次有新增的函数时,需要在字典里机里面再加入。能否做过,不论引入的多少函数,都可以直接预使用呢?再看下看。
3、getattr处理
代码如下:
#!/usr/bin/env python # coding=utf-8 ## author: yangbk(itybku@139.com) ## site: www.361way.com class getfun: def funa(self): print 'processNavigate' def funb(self): print 'processCreateTab' action = raw_input('请输入地址:') newfun = getfun() fun = getattr(newfun,action) fun()
这里我们使用的是类的作法,先创建一个类,将所有函数放在类里。在调用前先实例化该类,不论类里有多少个函数,直接根据输入内容通过getattr调用类里的函数。
4、getattr import引入
再所有的函数存在一个函数库funab.py里,如下:
#!/usr/bin/env python # coding=utf-8 def funa(): print 'running funa ......' def funb(): print 'running funb ......'
调用方法如下:
#!/usr/bin/env python # coding=utf-8 ## author: yangbk(itybku@139.com) ## site: www.361way.com action = raw_input('请输入地址:') newfun = __import__('funab') fun = getattr(newfun,action) fun()
这种写法和方法3基本上差别不大,不过是通过__import__ 的方法引入而已。
二、站点页面引用示例
getattr的另一个常用示例就是在多个web页面的情况下。在入口文件里经常需要用到getattr函数智能处理不同的页面请求。先看下一个常见的页面结构:
# tree . ├── backend │ ├── account.py │ ├── account.pyc │ ├── admin.py │ ├── admin.pyc │ ├── __init__.py │ └── __init__.pyc ├── index.py -----> if版入口文件 └── web.py -----> getattr版入口文件
admin.py和account代码比较简单,单纯为了测试,这里只是print内容。如下:
[root@361way backend]# cat admin.py #!/usr/bin/env python # coding=utf-8 def index(): print 'welcome to your, administrator !' [root@361way backend]# cat account.py #!/usr/bin/env python # coding=utf-8 def login(): print 'login now ******* ' def logout(): print 'logout now ******* '
先看下if版的用法:
[root@361way web]# cat index.py #!/usr/bin/env python # coding=utf-8 from backend import account data = raw_input('请输入地址:') if data == 'account/login': account.login() elif data == 'account/logout': account.logout()
这里只import了account ,对登陆和登出做了判断。如果需要登陆后台,还要import admin,并对admin里的方法再做判断。同样如果有还blog 、bbs 、usercenter等相关N个页面时,这个判断结构是不可想象的。如何通过getattr的方法处理实现呢?
[root@361way web]# cat web.py #!/usr/bin/env python # coding=utf-8 ## author: yangbk(itybku@139.com) ## site: www.361way.com ''' from backend import account data = raw_input('请输入地址:') if data == 'account/login': account.login() elif data == 'account/logout': account.logout() ''' data = raw_input('请输入地址:') array = data.split('/') userspance = __import__('backend.'+array[0]) model = getattr(userspance , array[0]) func = getattr(model,array[1]) func()
便于对比,老的if判断的代码我也粘了进去。实际有用的只有后面的一小部分。这里先通过输入内容通过split进行了切分,如输入的account/login ,会切分成数组[ account, login ] ,而输入是admin/index则切分成[ admin, index ] 。由于引用的函数在当前目录下的子目录下,所以这里需要通过backend连接引入。
至于这里为什么是两次getattr调用才能将结果输出呢?这里其实可以使用入门篇里的dir方法查看,也可以通过使用inspect模块进行判断、获取对象。通过dir分析结果如下:
>>> userspance = __import__('backend.'+ 'admin') >>> dir(userspance) ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', 'admin'] >>> model = getattr(userspance,'admin') >>> dir(model) ['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'index'] >>> getattr(model,'index') <function index at 0x7f572801a9b0> >>> getattr(model,'index')() welcome to your, administrator !
再看看直接执行后的效果:
[root@361way web]# python web.py 请输入地址:admin/index welcome to your, administrator ! [root@361way web]# python web.py 请输入地址:account/login login now ******* [root@361way web]# python web.py 请输入地址:account/logout logout now *******
是不是效果非常好,在backend里论有多少个页面,都可以通过这一段完成调用。完全不用做一大堆的import和if 判断。这里之所以和上面不一样两次getattr,是由于引用内容在子目录下。
三、getattr与传参
在定义getattr相关的函数时,如果import的模块内的函数有参数可传时,也可以通过如下的方法将参数传入。下面这个定义是网上查到的有人对django中的模块引导修改的一个getattr方法。