python-聊聊反射
Posted 楚时邀月
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python-聊聊反射相关的知识,希望对你有一定的参考价值。
反射
对于初学python可能较难理解,但反射是非常有用。
试想一下,当别的程序传入给你写的这段代码一个变量(var=“math”),这个变量是一个字符串,这个字符串是一个模块或者一个模块下的某个方法,你需要通过变量来导入此模块或者方法,如何导入此模块或方法呢,如果直接执行 import var是会出错的,因为var在你的这段代码中是一个变量, 这时就需要反射, 如何使用反射呢。
1.聊聊自省
在计算机编程中,自省是指这种能力:检查某些事物以确定它是什么、它知道什么以及他能做什么。自省向程序员提供了极大的灵活性和控制力。
几个重要的函数:
dir函数,传入的参数是对象,返回该对象的所有属性和函数列表:
>>> dir(str)
[\'__add__\', \'__class__\', \'__contains__\', \'__delattr__\', \'__doc__\', \'__eq__\', \'__format__\', \'__ge__\', \'__getattribute__\', \'__getitem__\', \'__getnewargs__\', \'__getslice__\', \'__gt__\', \'__hash__\', \'__init__\', \'__le__\', \'__len__\', \'__lt__\', \'__mod__\', \'__mul__\', \'__ne__\', \'__new__\', \'__reduce__\', \'__reduce_ex__\', \'__repr__\', \'__rmod__\', \'__rmul__\', \'__setattr__\', \'__sizeof__\', \'__str__\', \'__subclasshook__\', \'_formatter_field_name_split\', \'_formatter_parser\', \'capitalize\', \'center\', \'count\', \'decode\', \'encode\', \'endswith\', \'expandtabs\', \'find\', \'format\', \'index\', \'isalnum\', \'isalpha\', \'isdigit\', \'islower\', \'isspace\', \'istitle\', \'isupper\', \'join\', \'ljust\', \'lower\', \'lstrip\', \'partition\', \'replace\', \'rfind\', \'rindex\', \'rjust\', \'rpartition\', \'rsplit\', \'rstrip\', \'split\', \'splitlines\', \'startswith\', \'strip\', \'swapcase\', \'title\', \'translate\', \'upper\', \'zfill\']
可以看出,string对象的所有函数,属性都列举出来了。
getatter方法,传入参数是对象和该对象的函数或者属性的名字,返回对象的函数或者属性实例,如下:
>>> getattr(\'str\',\'rfind\') <built-in method rfind of str object at 0x7fc2e04c5b10>
callable方法,如果传入的参数是可以调用的函数,则返回true,否则返回false。
>>> callable(getattr(\'str\',\'rfind\')) True >>> callable(getattr(\'str\',\'__doc__\')) False >>>
列出对象的所有可以执行的函数:这个地方用到了列表推导式:
>>> methodLIst = [method for method in dir(str) if callable(getattr(str,method))] >>> print methodLIst [\'__add__\', \'__class__\', \'__contains__\', \'__delattr__\', \'__eq__\', \'__format__\', \'__ge__\', \'__getattribute__\', \'__getitem__\', \'__getnewargs__\', \'__getslice__\', \'__gt__\', \'__hash__\', \'__init__\', \'__le__\', \'__len__\', \'__lt__\', \'__mod__\', \'__mul__\', \'__ne__\', \'__new__\', \'__reduce__\', \'__reduce_ex__\', \'__repr__\', \'__rmod__\', \'__rmul__\', \'__setattr__\', \'__sizeof__\', \'__str__\', \'__subclasshook__\', \'_formatter_field_name_split\', \'_formatter_parser\', \'capitalize\', \'center\', \'count\', \'decode\', \'encode\', \'endswith\', \'expandtabs\', \'find\', \'format\', \'index\', \'isalnum\', \'isalpha\', \'isdigit\', \'islower\', \'isspace\', \'istitle\', \'isupper\', \'join\', \'ljust\', \'lower\', \'lstrip\', \'partition\', \'replace\', \'rfind\', \'rindex\', \'rjust\', \'rpartition\', \'rsplit\', \'rstrip\', \'split\', \'splitlines\', \'startswith\', \'strip\', \'swapcase\', \'title\', \'translate\', \'upper\', \'zfill\']
2.python是如何体现反射的
globals()
这个函数返回一个map,这个map的key是全局范围内对象的名字,value是该对象的实例,在不导入任何module下,执行globals()的结果如下:
>>> globals() {\'__builtins__\': <module \'__builtin__\' (built-in)>, \'__name__\': \'__main__\', \'__doc__\': None, \'__package__\': None} >>>
在导入sys后,可以发现,globals()返回的map中,多了sys module:
>>> globals() {\'__builtins__\': <module \'__builtin__\' (built-in)>, \'__name__\': \'__main__\', \'sys\': <module \'sys\' (built-in)>, \'__doc__\': None, \'__package__\': None}
在导入sgmlib如下:
>>> import sgmllib >>> globals() {\'sgmllib\': <module \'sgmllib\' from \'/usr/lib64/python2.6/sgmllib.pyc\'>, \'__builtins__\': <module \'__builtin__\' (built-in)>, \'__package__\': None, \'sys\': <module \'sys\' (built-in)>, \'__name__\': \'__main__\', \'__doc__\': None}
如果导入类后,在map中,可以找到类SGMLParser。
>>> from sgmllib import SGMLParser >>> globals() {\'sgmllib\': <module \'sgmllib\' from \'/usr/lib64/python2.6/sgmllib.pyc\'>, \'SGMLParser\': <class sgmllib.SGMLParser at 0x7fbd2754c530>, \'__builtins__\': <module \'__builtin__\' (built-in)>, \'__package__\': None, \'sys\': <module \'sys\' (built-in)>, \'__name__\': \'__main__\', \'__doc__\': None}
所以,只要将class的名字作为key,即可得到class。如下:
>>> globals()[\'SGMLParser\'] <class sgmllib.SGMLParser at 0x7fbd2754c530> >>> parser = globals()[\'SGMLParser\']() >>> parser <sgmllib.SGMLParser instance at 0x7fbd2d64f1b8>
将模块引入,之后确定模块的相应类的名字,就可以得到了一个类对象,之后就可以实例化了。
3.反射详解
python中的反射功能是由以下四个内置函数提供:hasattr、getattr、setattr、delattr,改四个函数分别用于对对象内部执行:检查是否含有某成员、获取成员、设置成员、删除成员。
#!/usr/bin/env python #-*- coding:utf-8 -*- __author__ = \'wyf\' class Foo(object): def __init__(self): self.name = \'chushiyaoyue\' def func(self): return \'func\' obj = Foo()#实例化一个对象 # #### 检查是否含有成员 #### hasattr(obj, \'name\') #True hasattr(obj, \'func\') #True # #### 获取成员 #### getattr(obj, \'name\') getattr(obj, \'func\') #chushiyaoyue #<bound method Foo.func of <__main__.Foo object at 0x0000000002C5F4A8>> # #### 设置成员 #### setattr(obj, \'age\', 18)#添加属性 setattr(obj, \'show\', lambda num: num + 1)#添加方法 # print vars(obj) #{\'age\': 18, \'name\': \'chushiyaoyue\', \'show\': <function <lambda> at 0x0000000002A46128>}#已经添加了一个属性和一个方法 # # #### 删除成员 #### delattr(obj, \'name\') delattr(obj, \'func\')
__import__(\'模块名字\')
from 包 import 模块名 as 别名 ====等于=== 别名 = __import__("模块名的字符串")
针对该模块同一级下另外一个目录,也就是包,而这个包下有另一个包,而我们需要导入的模块还在其下面,这时候,不能应用包.包.模块作为字符串传入__import__来导入了,因为其只会导入第一层包,需要加入一个参数fromlist = True
a = __import__(\'包\')#基础的导入 __import__("a.b.c.file.login",fromlist=True)#多层级的导入 __import__("a.b.c.file.login") #是不对的 只导入了a目录 包
python中一切实物都是对象,类,模块也是对象!反射是通过字符串的形式操作对象相关的成员!
类是对象:
1 #!/usr/bin/env python 2 #-*- coding:utf-8 -*- 3 __author__ = \'wyf\' 4 5 class Foo(object): 6 7 staticField = "test123" 8 9 def __init__(self): 10 self.name = \'chushiyaoyue\' 11 12 def func(self): 13 return \'func\' 14 15 @staticmethod 16 def bar(): 17 return \'bar\' 18 19 print getattr(Foo, \'staticField\') 20 print getattr(Foo, \'func\') 21 print getattr(Foo, \'bar\')
模块是对象:
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 def dev(): 5 return \'dev\'
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 """ 5 程序目录: 6 home.py 7 index.py 8 9 当前文件: 10 index.py 11 """ 12 13 14 import home as obj 15 16 #obj.dev() 17 18 func = getattr(obj, \'dev\') 19 func()
实例小练习:根据用户输入的url执行相关的功能
1 #!/usr/bin/env python 2 #-*- coding:utf-8 -*- 3 __author__ = \'wyf\' 4 5 def login(): 6 return \'login\' 7 8 def logout(): 9 return \'logout\' 10 11 def nb(): 12 return \'特别牛逼的页面\'
最简单粗暴的方法,简单很容易想到,但是如果方法一多,你要写很多代码
1 #!/usr/bin/env python 2 #-*- coding:utf-8 -*- 3 __author__ = \'wyf\' 4 5 from lib import account 6 7 url = raw_input(\'请输入url:\') 8 9 if url.endswith(\'login\'): 10 r = account.login() 11 print r 12 13 elif url.endswith(\'logout\'): 14 r = account.logout() 15 print r 16 17 elif url.endswith(\'nb\'): 18 r = account.nb() 19 print r 20 21 else: 22 print \'404\'
使用反射的方式,一步到位不管你有多少个方法调用,我都支持
1 #!/usr/bin/env python 2 #-*- coding:utf-8 -*- 3 __author__ = \'wyf\' 4 5 from lib import account 6 url = raw_input(\'请输入url:\') 7 target_module,target_func = url.split(\'/\') 8 9 m = __import__(\'lib.\',target_module,fromlist=True) 10 if hasattr(m,target_func):#判断这个函数里面是否存在这个方法 11 target_func = getattr(account,inp)#获取这个方法 12 r = target_func()#执行这个方法 13 print r 14 else: 15 print \'404\'
以上是关于python-聊聊反射的主要内容,如果未能解决你的问题,请参考以下文章