首页 > 代码库 > python中的反射

python中的反射

        在绝大多数语言中,都有反射机制的存在。从作用上来讲,反射是为了增加程序的动态描述能力。通俗一些,就是可以让用户参与代码执行的决定权。在程序编写的时候,我们会写很多类,类中又有自己的函数,对象等等。这些类和函数都是为了后序代码服务,程序员决定什么时候用到哪一个类,什么时候调用某个函数。但很多时候,我们需要根据用户的需求来决定执行哪一段代码块。用户可能是通过点击,输入数据,或者其他方式发出指令,反射则将用户的指令传递到需要执行的那一段代码块。这个过程是自动执行的,无需人工去核对用户指令是否应该执行那一段代码,而是由反射机制自动查找该执行的代码块。大多数反射都是以web来进行举例说明,而反射本身的最常见的使用场景也确实是根据web的url不同来调用不同的函数。当然这里,我们不用讨论他的具体应用,只简单说明一下他的使用意义。

        python的反射机制设定较为简单,一共有四个关键函数分别是getattr、hasattr、setattr、delattr。前两个最为常用,最后一个几乎很少用到。python本身定义的反射是指在内存中对容器里的某些元素进行操作,这个容器不仅仅包括类,还包括函数,对象,这三者不同的是在查找对象的时候,除了会查找对象自身,还会去创建对象的类里面进行查找。要用实际例子来说明一下python中的反射具体作用,先看一下需求。所有的语言中,我们都可以轻易办到让用户自由输入一个数据,然后打印那个数据,这是最简单的人机交互。在代码里的实现过程是,生成一个变量,获取用户输入数据,赋值给变量。打印变量。同理我们可以在某个类中定义两个函数,然后要求用户输入数据,根据用户输入的数据来决定具体执行哪一个函数,这就是一个人工的反射机制。当需要查找的函数只有两条的时候,我们可以用if——else进行判断。但如果数据达数百条之多,那重复性使用if不仅效率低下,而且代码量也难以估量。这种情况,就需要用到反射。具体代码如下:

#新建一个方法类 命名为echo_test 类中代码如下,定义三个函数,函数内容为打印函数名
__author__ = "lixin"
def echo_test1():
    print("echo_test1")
def echo_test2():
    print("echo_test2")
def echo_test3():
    print("echo_test3")
 
#另一个执行类 代码如下
__author__ = "lixin" #
import echo_test 导入方法类
func_name = input("What do you want to do?,please enter:")#请求用户输入数据
func = getattr(echo_test,func_name)#调用getattr函数,参数分别是方法类的类名,用户输入的数据。生成变量接收返回参数
func()#把变量当做方法执行

#输出结果如下
/Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /Users/penglong/Documents/python/s11/day8/echo_example/echo_func.py
What do you want to do?,please enter:echo_test1#输入数据
echo_test1#执行结果

Process finished with exit code 0

  

  来分析一下代码,首先说代码仅做实例,极为不完善,且输入数据只能是定义的函数的名称,但我们主要是讨论getattr的作用。从代码不难看出,它接收一个类名和一个字符串做为参数,然后去给的类里查找和字符串相同的函数名,并将那个函数的全部内容返回。所以,我们的变量实际是一个函数,因此可以直接调用。这就是一个简单的完整反射。也是python中反射最主要的功能。hasatter的作用更多是为了getattr服务。就如同上面的代码中,有可能用户输入的数据,在我们的定义的函数中并没有与之匹配的函数名,那也就无法执行。如果没有错误防御机制,程序就会崩溃,因此拿到用户输入的数据,在直接去查找执行前,需要先判断一下用户想要执行的函数是否存在,这就是hasattr的作用了  代码如下:

#在上一份代码的基础上直接更改,注意是在交互类
func_name = input("What do you want to do?,please enter:")
func = hasattr(echo_test,func_name)#getattr改为hasatter
print(func)
 输出结果如下:
/Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /Users/penglong/Documents/python/s11/day8/echo_example/echo_func.py
What do you want to do?,please enter:echo_test1
True #返回结果为布尔值

Process finished with exit code 0

  

       以上,可以看出,hasattr函数的参数跟getattr是一样的,接收一个类和一个字符串,返回一个布尔值。它的作用就是检测用户输入的内容是否有对应的函数存在。如果有,返回true,没有,则false。我们则可以根据结果预防找不到函数的错误。因此,它和getattr常常配套使用,如果判定存在,则获取,再执行,这样可以保证代码不会运行出错。setattr的作用则是创建一个对象,代码如下:

  

#还是在原代码基础上修改 ,交互类
print(dir(echo_test))#首先输出一下echo_test这个类的所有方法
func_name = input("What do you want to do?,please enter:")#请求用户输入数据
func = setattr(echo_test,func_name,lambda x:x+1)#调用setattr函数,参数分别是类名,用户输入数据,一个简单函数
print(dir(echo_test))#再次打印echo_test这个类的所有方法

"""输出结果如下
第一次打印类方法结果
[‘__author__‘, ‘__builtins__‘, ‘__cached__‘, ‘__doc__‘, ‘__file__‘, ‘__loader__‘, ‘__name__‘, 
‘__package__‘, ‘__spec__‘, ‘echo_test1‘, ‘echo_test2‘, ‘echo_test3‘] 

What do you want to do?,please enter:lixin
第二次打印结果
[‘__author__‘, ‘__builtins__‘, ‘__cached__‘, ‘__doc__‘, ‘__file__‘, ‘__loader__‘, ‘__name__‘,
 ‘__package__‘, ‘__spec__‘, ‘echo_test1‘, ‘echo_test2‘, ‘echo_test3‘, ‘lixin‘]"""

  

        从输出结果分析,setattr函数的作用就是新建一个对象,参数分别是新建对象所属类的类名,新建对象的对象名,对象的值,这个值可以是字符串,也可以是数字,当然也可以是一个函数,上面代码为了简便,直接用了一个匿名函数。最后一个delattr则是删除存在的函数,使用率较低,也没什么特别注意的地方。

       在使用过程中,还需要提到的就是动态获得类名,如上所有代码中的类名,都是我们固定输入的,在实际运用当中,这样会使代码极为不灵活。四个反射函数的第一个参数都只接受类名,而无法接收字符串。用户直接输入的数据,格式显然是字符串,因此无法直接使用。当然,我们可以把字符串转化成类名,但无需那么麻烦。python有相应的应对措施,使用代码如下:

#原代码基础上修改,交互类
__author__ = "lixin" func_name = input("What do you want to do?,please enter:")#获取用户输入数据 class_name,func_name = func_name.split("/")将用户输入数据分割,并分别赋值给两个变量 model = __import__(class_name)#以__import__的形式导入类名,并生成变量获取返回值 Flog = hasattr(model,func_name)#变量值可以直接当做参数传入,在这里验证一下函数是否存在 if Flog:#如果存在在调用getattr函数,如果不存在,则提示数据有误。 func = getattr(model, func_name) func() else: print("输入有误") """输出结果如下: /Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /Users/penglong/Documents/python/s11/day8/echo_example/echo_func.py What do you want to do?,please enter:echo_test/echo_test1#输入内容,类名和函数名以/隔开 echo_test1 Process finished with exit code 0"""

       以上,就是python中反射的常用方法了,当然在最后一段代码中,我们应该验证一下类是否存在,但python中并没有针对这个的函数了,举例中也并未给出防御机制,在实际使用中,肯定是不行的,因为类不存在,代码也无法运行,所以也要给出相应的错误防御机制。除了在类中查找函数,反射自然也能用于在函数中查找对象,在对象身上查找属性,自身方法等等。这一些操作,都是立足于内存上,而不是对代码本身进行操作。

  

python中的反射