Please enable Javascript to view the contents

Python的反射:getattr

 ·  ☕ 3 分钟

1. 应用场景

  1. 通过配置文件,控制程序运行时的流程。配置文件中常保存的是,字符串,而不是对象
  2. 调试程序时,查看对象的全部属性值
  3. 动态模块的导入

对于第一种场景,广泛被采用的是反射。在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能够实现相同的功能,但是反射是一种编程方法、是设计模式的体现。反射凝聚了高内聚、低耦合的软件设计思想,不能简单的使用执行字符串的函数替代。


微信公众号
作者
微信公众号