Python 反射机制

2017-05-31  本文已影响0人  Chenzongshu

有时候我们会碰到这样的需求,需要执行对象的某个方法,或是需要对对象的某个字段赋值,而方法名或是字段名在编码代码时并不能确定,需要通过参数传递字符串的形式输入。举个具体的例子:当我们需要实现一个通用的DBM框架时,可能需要对数据对象的字段赋值,但我们无法预知用到这个框架的数据对象都有些什么字段,换言之,我们在写框架的时候需要通过某种机制访问未知的属性

这个机制被称为反射,用于实现在运行时获取未知对象的信息

1.访问对象的属性

#coding: UTF-8
import sys #  模块,sys指向这个模块对象
import inspect
def foo(): pass # 函数,foo指向这个函数对象
 
class Cat(object): # 类,Cat指向这个类对象
    def __init__(self, name='kitty'):
        self.name = name
    def sayHi(self): #  实例方法,sayHi指向这个方法对象,使用类或实例.sayHi访问
        print self.name, 'says Hi!' # 访问名为name的字段,使用实例.name访问
 
cat = Cat() # cat是Cat类的实例对象
 
print Cat.sayHi # 使用类名访问实例方法时,方法是未绑定的(unbound)
print cat.sayHi # 使用实例访问实例方法时,方法是绑定的(bound)

以下列出了几个内建方法,可以用来检查或是访问对象的属性。这些方法可以用于任意对象而不仅仅是例子中的Cat实例对象;
Python中一切都是对象。

cat = Cat('kitty')
 
print cat.name # 访问实例属性
cat.sayHi() # 调用实例方法
 
print dir(cat) # 获取实例的属性名,以列表形式返回
if hasattr(cat, 'name'): # 检查实例是否有这个属性
    setattr(cat, 'name', 'tiger') # same as: a.name = 'tiger'
print getattr(cat, 'name') # same as: print a.name
 
getattr(cat, 'sayHi')() # same as: cat.sayHi()

2.访问对象的元数据

当你对一个你构造的对象使用dir()时,可能会发现列表中的很多属性并不是你定义的。这些属性一般保存了对象的元数据,比如类的_name_属性保存了类名。大部分这些属性都可以修改,不过改动它们意义并不是很大;修改其中某些属性如function.func_code还可能导致很难发现的问题,所以改改name什么的就好了,其他的属性不要在不了解后果的情况下修改。

a).判断对象的类型

在types模块中定义了全部的Python内置类型

b).模块

import fnmatch as m
print m.__doc__.splitlines()[0] # Filename matching with shell patterns.
print m.__name__                # fnmatch
print m.__file__                # /usr/lib/python2.6/fnmatch.pyc
print m.__dict__.items()[0]     # ('fnmatchcase', <function>)</function>

c).类(class)

实例是指类实例化以后的对象。

print cat.__dict__
print cat.__class__
print cat.__class__ == Cat # True

d).内建函数和方法

e). 函数(function)

这里特指非内建的函数。注意,在类中使用def定义的是方法,方法与函数虽然有相似的行为,但它们是不同的概念。

#coding: UTF-8
def foo():
    n = 1
    def bar():
        print n # 引用非全局的外部变量n,构造一个闭包
    n = 2
    return bar
 
closure = foo()
print closure.func_closure
# 使用dir()得知cell对象有一个cell_contents属性可以获得值
print closure.func_closure[0].cell_contents # 2

f).方法(method)

方法虽然不是函数,但可以理解为在函数外面加了一层外壳;拿到方法里实际的函数以后,就可以使用2.5节的属性了。

im = cat.sayHi
print im.im_func
print im.im_self # cat
print im.im_class # Cat

g).

生成器(generator),代码块(code),栈帧(frame),追踪(traceback) 略

3. 使用inspect模块

a).检查对象类型

im = cat.sayHi
if inspect.isroutine(im):
    im()

b).获取对象信息

def add(x, y=1, *z):
    return x + y + sum(z)
print inspect.getargspec(add)
#ArgSpec(args=['x', 'y'], varargs='z', keywords=None, defaults=(1,))
def add(x, y=1, *z):
    print inspect.getargvalues(inspect.currentframe())
    return x + y + sum(z)
add(2)
#ArgInfo(args=['x', 'y'], varargs='z', keywords=None, locals={'y': 1, 'x': 2, 'z': ()})
print inspect.getmro(Cat)
#(<class '__main__.Cat'>, <type 'object'>)
print Cat.__mro__
#(<class '__main__.Cat'>, <type 'object'>)
class Dog: pass
print inspect.getmro(Dog)
#(<class __main__.Dog at 0x...>,)
print Dog.__mro__ # AttributeError
上一篇 下一篇

猜你喜欢

热点阅读