python中的反射

什么是反射?

反省、自省。

反射指的是一个对象具有检测、访问、修改自身属性的能力

反射本质上就是在使用四个内置函数,然后对属性进行增啥改查。

class A:
    x = 1
    def f(self):
        print('我是函数f')

a = A()

hasattr(object, '属性名'): 查看对象object中有没有’属性名‘这个属性。因为类也是一个对象,所以第一个参数也可以直接是类名。一切皆对象:所有对象都可以反射。返回值:bool


bool_value = hasattr(a, 'x')
print(bool_value)  #True

bool_value = hasattr(A, 'x')
print(bool_value)  #True

getattr(objcet, '属性名', '没有这个属性时的默认返回值'):

相当于:obj.属性

参数:第三个参数,指的是object中没有指定的字符串属性时,会默认将第三个参数返回。

返回值:返回对象里面的一个属性

x = getattr(a, 'x')
print(x)  # 1

f = getattr(a, 'f')
print(f)  # <bound method A.f of <__main__.A object at 0x10da19b10>>
f()  # 我是函数f

setattr(object, '属性名', '属性值'):

相当于:obj.属性 = 值

第一个参数:反射的对象

第二个参数:反射的对象的属性名

第三个参数:要修改的反射的对象的值

返回值:None

setattr(A, 'x', '设置后的值')
print(A.x) # 设置后的值
print(a.x) #设置后的值

delattr(object, '要删除的属性'):

返回值None

delattr(A, 'x')
print(A.x) 
#    print(A.x)
# AttributeError: type object 'A' has no attribute 'x'

反射的使用场景

1、对属性进行增删改查,使用对象 __ dict _ 也可以对属性进行增删改查,但是语法繁琐,不好理解。

2、如果这个对象不是自己写的,就必须判断这个对象是否满足,也就是是否是我需要的属性。

if hasattr(a, 'f'):
    func = getattr(a, 'f')
    func()
else:
    print('f这个函数不存在时要处理的逻辑')
    
# 确认A类里面的有没有f这个函数名,, 这时候调用
	
from module import A

# 在你的代码中,需要用到别人的代码,但是别人出去玩了代码没写完。这个时候就可以利用反射只定义接口,后期再完善。

a = A()
if hasattr(a, 'test'):
    #得到a的test方法
    test = getattr(a, 'test')

else:
    print('不存在此方法,执行其他的逻辑')
#反射当前模块
import sys

def s1():
    print('s1')
x
def s2():
    print('s2')


this_module = sys.modules[__name__]

hasattr(this_module, 's1')
s = getattr(this_module, 's2')
s()
#动态导入
import importlib

# 拿到模块,根据模块的路径
plugin = importlib.import_module('lib.plugins')
print(plugin)
# 从模块中取出类
cls = getattr(plugin, 'WinCMD')
# obj = cls()
# obj.cd()

s = setattr(cls, 'x', 'cdd')
print(s)
print(cls.x)

反射机制

#写一个函数
def func():
    print('我是一个函数')
#写一个字符串
s = 'func'

我们可以通过 func+()的方式调用func这个函数,但是我们却不能用字符串 'func'()的形式调用func函数。说白了就是:不能通过字符串来调用名字看起来相同的函数!

反射机制要做的事就是:让字符串可以直接调用名字相同的函数!

func = getattr(object, s)是关键,通过getattr函数,从object里面查找名字跟s相同的函数名(这里s = 'func'),然后将函数返回赋值给func变量,这时候func+()就能直接调用object里面的函数。

getattr做的事,其实就是把一个字符串转为一个函数的过程。这里就是把字符串’func‘ 转为了函数 func

实现网站url路由功能

#网站的视图 views.py 文件

def login():
    print('我是登录页面')


def logout():
    print('我是退出')


def index():
    print('我是网站首页')

#运行文件 start.py
import views

def run():
    while True:
        '''
        url = 'www.baidu.com/index'
        这个url指向的视图为index
        '''
        url = input('输入要跳转的路径:').strip()
        view = url.split('/')[-1]

        if view == 'index':
            views.index()

        if view == 'login':
            views.login()

        if view == 'logout':
            views.logout()

上面模拟了一个简单的路由系统,可以看到,url的最后一个 / 后面的内容,是views.py 视图文件里面的一个方法。上面有三个视图,所以写了三个路由的if语句。试想一下,如果一个网站有几百上千的url,是不是要写上千的 if语句? 显然:这个不科学的。

由于 url 的最后一个 / 后面的字符串,跟视图文件里面的函数名相同,我们可不可以直接把字符串当做函数来用?这个时候,反射机制就可以大显身手了。

对执行文件稍作修改:

#运行文件 start.py
import views

def run():
    while True:
        '''
        url = 'www.baidu.com/index'
        这个url指向的视图为index
        '''
        url = input('输入要跳转的路径:').strip()
        view = url.split('/')[-1]

        if hasattr(views, view):
            func = getattr(views, view) # getattr如果对象里面没有与字符串相同属性就会报错,所以需要先hasattr一下,查看字符串对应的属性存不存在。
            func()
        else:
            print('url 404 ')

上面的这种情况只针对,运行文件start.py和视图文件views.py在同一个文件夹下的情况。在实际项目中,视图文件都是分散在各个不同目录的不同模块中的。为了解决这个问题,可以使用 __ import __ 解决。

__ import __ (字符串参数), __ import __ 会根据字符串参数,动态导入同名的模块。

项目路径示意:

-- my_app

--|—user.py

--|—|—index()

--|—|—check_info()

—|—core.py

--|—|— home() #跳转到 home

在此修改 start.py 文件的代码:

#start.py
def run():
    while True:
        '''
        url = 'www.baidu.com/index'
        这个url指向的视图为index
        '''
        url = input('输入要跳转的路径:').strip()
        modules, view = url.split('/')   #user/index  core/home

        views = __import__(modules)  #动态导入同名模块,不用再手动导入了。

        if hasattr(views, view):
            func = getattr(views, view)
            func()
        else:
            print('url 404 ')

如果 user.py 和 core.py 在一个 lib 包下面,跟启动文件start.py不在同一个目录下,如下所示:

my_app

—lib

—|—user.py

—|—|—index() #跳转到用户首页

—|—|—check_info()

—|—core.py

—|—|— home() #跳转到 home

—|—start.py


def run():
    while True:
        '''
        url = 'www.baidu.com/index'
        这个url指向的视图为index
        '''
        url = input('输入要跳转的路径:').strip()
        modules, view = url.split('/')   #user/index  core/home
		#自然的会想到在modules前面加个lib
        #看起来似乎没什么问题,和import lib.core的传统方法类似,但实际上运行的时候会有错误。__import__需要加个参数  fromlist=True
        views = __import__('lib.' + modules, fromlist=True)

        if hasattr(views, view):
            func = getattr(views, view)
            func()
        else:
            print('url 404 ')

posted @ 2019-07-31 21:55  KbMan  阅读(313)  评论(0编辑  收藏  举报