Python反射机制与动态模块导入

Python反射,可以理解为利用字符串的形式去操作对象中的成员属性和方法。

考虑有这么一个场景,根据用户输入的url的不同,调用不同的函数,实现不同的操作:

# commons.py
def login():
    print('登录页面!')
 
def logout():
    print('退出页面!')
 
def index():
    print('主页面!')
 
# index.py
import commons
 
def run():
    inp = input("请输入您想访问页面的URL:").strip()
    if inp == "login":
        commons.login()
    elif inp == "logout":
        commons.logout()
    elif inp == "index":
        commons.index()
    else:
        print("404")
 
if __name__ == '__main__':
    run()

我们运行index.py,输入:index,页面结果如下:

$ python3 index.py
请输入您想访问页面的URL:index
主页面!

这就实现了一个简单的WEB路由功能,根据不同的url,执行不同的函数,获得不同的页面。那现在如果我的网站内容变多了,在commons.py中有100个页面操作,那么相对应的我在index.py中也要使用if else对这100个页面函数进行手动指定。显然这是不可能的!那么怎么破?这个时候Python反射特性就可以排上用场了。

# index.py
import commons
 
def run():
    inp = input('请输入您想访问页面的URL:').strip()
    if hasattr(commons, inp):
        func = getattr(commons, inp)
        func()
    else:
        print('404')
 
if __name__ == "__main__":
    run()

现实使用环境中,页面处理函数往往被分类放置在不同目录的不同模块中。比如,现在我又新增了一个account.py这个用户管理类的文件,也需要导入到首页以备调用。

__import__方法会根据字符串参数,动态的导入同名的模块。

# account.py
def add_user():
    print('添加用户')
 
def del_user():
    print('删除用户')
 
# commons.py
def login():
    print('登录页面!')
 
def logout():
    print('退出页面!')
 
def index():
    print('主页面')
 
# index.py
def run():
    inp = input('请输入您想访问页面的URL:').strip()
    m, f = inp.split('/')
    obj = __import__(m)
    if hasattr(obj, f):
        func = getattr(obj, f)
        func()
    else:
        print('404')
 
if __name__ == "__main__":
    run()

这里有个小瑕疵!如果我的项目进一步细化分工,现在又多了一层目录结构,如下所示:

|- index.py
|- commons.py
|- account.py
|- lib
	|- __init__.py
	|- connectdb.py

connectdb.py有如下方法:

def mysql():
    print('连接成功!')

这个问题怎么解决呢?__import__函数中有一个fromlist参数,源码解释说,如果在一个包中导入一个模块,这个参数如果为空,则return这个包对象,如果这个参数不为空,则返回包下面指定的模块对象,于是做出如下修改:

# index.py
def getf(m, f):
    if hasattr(m, f):
        func = getattr(m, f)
        func()
    else:
        print('404')
 
def run():
    if len(inp.split('/')) == 2:
        m, f = inp.split('/')
        obj = __import__(m)
        getf(obj, f)
    elif len(inp.split('/')) == 3:
        p, m, f = inp.split('/')
        obj = __import__(p + '.' + m, fromlist=True)
        getf(obj, f)
    else:
        print('404')
 
if __name__ == "__main__":
    inp = input('请输入您想访问页面的URL:')
    run()

现在运行测试:

$ python3 index.py
请输入您想访问页面的URL:commons/index
主页面!

$ python3 index.py
请输入您想访问页面的URL:lib/connectdb/mysql
连接成功!

进一步改进:

# index.py
def getf(m, f):
    if hasattr(m, f):
        func = getattr(m, f)
        func()
    else:
        print('404')
 
def run():
    t = inp.split("/")
    f = t[-1]
    p = '.'.join(t[0:-1])
    obj = __import__(p, fromlist=True)
    getf(obj, f)
    else:
        print('404')
 
if __name__ == "__main__":
    inp = input('请输入您想访问页面的URL:')
    run()

exec和eval也有它的舞台,在web框架里也经常被使用。

Reference: 运维那点事

猜你喜欢

转载自blog.csdn.net/azenlijing/article/details/129780191