Python 反射

反射说简单点 --> 就是利用字符串的形式去对象(模块)中操作(寻找/检查/删除/设置)成员。

1.根据字符串的形式导入模块。
2.根据字符串的形式去对象(某个模块)中操作其成员 

说反射之前先介绍一下__import__方法,这个和import导入模块的另一种方式

1. import  commons
2. __import__('commons') 

如果是多层导入:

1. from list.text import commons 
2. __import__(' list.text.commons',fromlist=True) #如果不加上fromlist=True,只会导入list目录

反射即想到4个内置函数分别为:getattr、hasattr、setattr、delattr  获取成员、检查成员、设置成员、删除成员下面逐一介绍先看例子:

class Foo():
    def __init__(self):
        self.name = 'abc'

    def func(self):
        return "OK"


obj = Foo()
# 获取成员
ret = getattr(obj, 'func')  # 获取的是个对象
r = ret()
print(r)

# 检查成员
ret = hasattr(obj, 'func')  # 因为有func方法所以返回True
print(ret)

# 设置成员
print(obj.name)  # 设置之前为:abc
ret = setattr(obj, 'name', 19)
print(obj.name)  # 设置之后为:19


# 删除成员
print(obj.name)  # abc
delattr(obj, 'name')
#print(obj.name)  # 报错
OK
True
abc
19

 web实例

  考虑有这么一个场景,根据用户输入的url的不同,调用不同的函数,实现不同的操作,也就是一个url路由器的功能,这在web框架里是核心部件之一。下面有一个精简版的示例:

  首先,有一个test模块,它里面有几个函数,分别用于展示不同的页面,代码如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author  : Jack.Ming

def login():
    print("这是一个登陆界面")

def logout():
    print("这是一个退出界面")

def look():
    print("这是一个浏览界面")

其次,有一个web模块,作为程序入口,接受用户输入,展示相应的页面,代码如下:(这段代码是比较初级的写法)

import test

def run():
    inp = input("请输入您想访问页面的url: ").strip()
    if inp == "login":
        test.login()
    elif inp == "logout":
        test.logout()
    elif inp == "home":
        test.look()
    else:
        print("404")

if __name__ == '__main__':
    run()

运行该程序:

请输入您想访问页面的url:  login
这是一个登陆界面

这就实现了一个简单的WEB路由功能,根据不同的url,执行不同的函数,获得不同的页面。

如果在text模块有成千上万了函数,那么在web模块中也需要有成千上万个判断语句,那这样岂不是很麻烦,我们可以用反射的方式解决这种问题:

反射机制(动态导入)

仔细观察web中的代码,我们会发现用户输入的url字符串和相应调用的函数名好像!如果能用这个字符串直接调用函数就好了!但是,前面我们已经说了字符串是不能用来调用函数的。为了解决这个问题,我们可以使用getattr和hasattr这两个内置函数,代码如下:

# !/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author  : Jack.Ming
def run():
    url = input("请输入URL地址:").strip()
    modules, index = url.split("/")
    obj = __import__(modules, fromlist=True)
    if hasattr(obj, index):
        func = getattr(obj, index)
        func()
    else:
        print("404 页面不存在")

if __name__ == '__main__':
    run()

运行:

请输入URL地址:test/login
这是一个登陆界面

分析一下以上代码:

  首先,我们并没有定义任何一行import语句;

  其次,用户的输入URL被要求为类似“commons/home”这种格式,其实也就是模拟web框架里的url地址,斜杠左边指向模块名,右边指向模块中的成员名。

  然后,modules,index = url.split("/")处理了用户输入,使我们获得的2个字符串,并分别保存在modules和index变量里。

  接下来,最关键的是obj = __import__(modules)这一行,它让程序去导入了modules这个变量保存的字符串同名的模块,并将它赋值给obj变量。

  使用hasattr方法判断在该模块中是否有该方法。

  最后的调用中,getattr去modules模块中调用index成员的含义和以前是一样的。

  总结:通过__import__函数,我们实现了基于字符串的动态的模块导入。

 利用反射查看面向对象成员归属

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2017/12/26 0026 15:18
# @Author  : ming

# 以字符串的形式去对象中查找成员
class Foo:
    def __init__(self, name):
        self.Name = name

    def show(self):
        print("show")


# 反射:类,只能查找类中的成员
r = hasattr(Foo, "Name")
print(r)
r = hasattr(Foo, "show")
print(r)

# 反射:对象,即可查找对象,也可以查找类的成员
obj = Foo("yang")
r = hasattr(obj, "Name")
print(r)
r = hasattr(obj, "show")
print(r)

运行结果:

False
True
True
True

 利用反射导入模块、查找类、创建对象、查找对象中的字段

内容如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/1/2 0002 14:34
# @Author  : ming

# 导入模块
m = __import__("s1", fromlist=True)

# 去模块中找类
lei = getattr(m, "Foo")

# 获取类对象
obj1 = lei("yang")

# 去类中找属性值Name
obj2 = getattr(obj1,"Name")

# print
print(obj2)
yang
原文地址:https://www.cnblogs.com/ming5218/p/8137130.html