Python 反射

getattr

def getattr(object, name, default=None):
    pass

参数:
	- 参数一:类对象或模块
    - 参数二:字符串

场景:根据用户输入来调用执行不同的函数,类似于 DjangoflaskWeb 框架的路由系统。

常规写法

1、common.py

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


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


def home():
    print("这是网站主页面!")

2、visit.py

import common


def run():
    inp = input('请输入你的 URL:').strip()
    if inp == "home":
        common.home()
    elif inp == "login":
        common.login()
    elif inp == "logout":
        common.logout()
    else:
        print('404 not found')


if __name__ == '__main__':
    run()

优缺点:

  • 优点:逻辑清晰,一眼即能理解
  • 缺点:common.py 有多少函数,就要判断多少次,代码重复性较高

getattr 反射写法

visit.py

import common


def run():
    inp = input('请输入你的 URL:').strip()
    func = getattr(common, inp)
    func()


if __name__ == '__main__':
    run()

优缺点:

  • 优点:简洁
  • 缺点:初学者可能不太容易理解

hasattr

参数与 getattr 一致,用于判断对象、模块是否具有某个字符串

def hasattr(*args, **kwargs):
	pass

上面 visit.py 有个缺点,当用户输入的 URLcommon.py 中没有时会报错:AttributeError: module 'commons' has no attribute 'xxx'.

这是因为 common.py 中没有 xxx() 函数所致,解决办法就是使用 hasattr() 判断是否有该函数,再 getarrt(),改造后如下:

import common


def run():
    inp = input('请输入你的 URL:').strip()
    if hasattr(common, inp):
        func = getattr(common, inp)
        func()
    else:
        print('404')

if __name__ == '__main__':
    run()

动态导入模块

上述示例固然好,但有一个缺点:就是 common.pyvisit.py 必须在同一级目录。而实际生产环境中,往往各个功能模块分布在不同文件夹中,若想实现上述功能,那么就需要在 visit.py 写入大量的 import 语句。

实际上可以通过 Python 的动态导入模块,动态导入要执行的功能函数模块,避免写过多的重复代码。

__import__(name)

Python内置的__import__(字符串参数)函数可以实现类似 getattr() 的反射功能。__import__()方法会根据字符串参数,动态地导入同名的模块。

1、目录结构:

│─account.py
│─common.py
│─manage.py
│─visit.py
│
└─__pycache__

2、visit.py

def run():
    inp = input('请输入你的 URL:').strip()
    modules, func = inp.split('/')		# common/home
    obj = __import__(modules)

    if hasattr(obj, func):
        func = getattr(obj, func)
        func()
    else:
        print('404')


if __name__ == '__main__':
    run()

Tips:输入的时候要同时提供模块名和函数名字,并用斜杠分隔


跨包问题

同样地,上述动态导入模块,当出现跨包时,也会导致出现模块导入问题,即 visit.py 与其他功能模块不在同一级目录。

1、目录结构:

│─visit.py
│
├─lib
│      account.py
│      common.py
│      manage.py
│
└─__pycache__

2、visit.py

def run():
    inp = input('请输入你的 URL:').strip()
    modules, func = inp.split('/')
    obj = __import__("lib." + modules)

    if hasattr(obj, func):
        func = getattr(obj, func)
        func()
    else:
        print('404')


if __name__ == '__main__':
    run()

运行结果:

请输入你的 URL:common/home
404

common.py 明明有 home() 函数,却提示 404。这是因为 __import__ 默认只会导入最开头的圆点左边的目录,也就是lib,后面的不会导入,除非加上fromlist=True 这个参数:

obj = __import__("lib." + modules, fromlist=True)

运行结果:

请输入你的 URL:common/home
这是网站主页面!

参考文章:http://www.liujiangblog.com/course/python/48

你可能感兴趣的:(反射,getattr)