1. 应用场景
- 通过配置文件,控制程序运行时的流程。配置文件中常保存的是,字符串,而不是对象
- 调试程序时,查看对象的全部属性值
- 动态模块的导入
对于第一种场景,广泛被采用的是反射。在Java的很多框架中都使用了反射机制,Python实现的Web框架Django中,也有应用。比如url的路由。第二种场景,手动添加输出某个对象object类的属性值,非常的麻烦,而且不全。这时,可以使用dir(object)列出全部属性,然后使用反射来访问。第三中场景,通过反射也能实现。
2. Python的反射
通过反射,可以将字符串和对象的属性关联起来。通过dir(object),可以查看对象obejct的属性列表。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| # -*- coding: utf-8 -*-
class Person:
'''
Python反射的一个举例
'''
count = 0
def __init__(self, name="none", salary="0"):
self.name = name
self.salary = salary
Person.count += 1
def getName(self):
print "i am in getName() function"
return self.name
>>> p = Person(name="csw")
>>> dir(p)
['__doc__', '__init__', '__module__', 'count', 'getName', 'name', 'salary']
>>> hasattr(p, 'count')
True
>>> getattr(p, 'count')
1
>>> getattr(p, 'getName')()
i am in getName() function
csw
>>> setattr(p, 'ModifiedName', 'name')
None
>>> delattr(p, 'name')
None
>>> dir(p)
['ModifiedName', '__doc__', '__init__', '__module__', 'count', 'getName', 'salary']
>>> getattr(p, 'getName')()
AttributeError: Person instance has no attribute 'name'
|
2.1 getattr
方式:getattr(object,‘name‘,‘default’)
说明:如果存在name的属性方法,则返回name的属性方法,否则返回default的属性方法。
2.2 hasattr
方式:hasattr(object, ’name‘)
说明:判断对象object是否包含名为name的属性方法,存在则返回True,否则返回False。hasattr是通过调用getattr(ojbect, ’name‘)是否抛出异常来实现)。
2.3 setattr
方式:setattr(object,‘name’,’default‘)
说明:设置对象object的name属性值为default,如果没有name属性,那么创建一个新的属性。
2.4 delattr
方式:delattr(object,’name’)
说明:删除对象object的name属性值。
3. Python的反射实现
globals( )函数返回字典{ key:value },key是对象的名称,value是对象的实例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| >>> globals()
{'__builtins__': <module '__builtin__' (built-in)>,
'__file__': 'E:/code/lab/python-reflection/exapmle.py',
'__package__': None, 'Person': <class __main__.Person at 0x00215FB8>,
'__name__': '__main__', '__doc__': None}
>>> import test
{'__builtins__': <module '__builtin__' (built-in)>,
'__file__': 'E:/code/lab/python-reflection/exapmle.py',
'__package__': None, 'Person': <class __main__.Person at 0x02045FB8>,
'test': <module 'test' from 'D:\Python2711\lib\test\__init__.pyc'>,
'__name__': '__main__', '__doc__': None}
>>> globals()['test']
<module 'test' from 'D:\Python2711\lib\test\__init__.pyc'>
|
在执行 import test
之后,可以看到新增了 test 的 key。可以利用这一点实现类似 Java 中,Class.forName()的功能。但是这种方法,使用之前必须先 import
,否则会抛出异常。
Python中提供了 __import__(“functionName”)函数,传入参数“functionName”,就可以导入functionName模块。然后结合getattr函数进行相关的调用。
4. 为什么不直接用 exec和eval
熟悉python的同学应该知道,exec和eval语句,也可以用来执行储存在字符串或文件中的Python语句。
比如:
1
2
3
4
| >>> eval('2*3')
6
>>> eval("2+3")
5
|
那么为什么还要反射呢?使用exec和eval能够实现相同的功能,但是反射是一种编程方法、是设计模式的体现。反射凝聚了高内聚、低耦合的软件设计思想,不能简单的使用执行字符串的函数替代。