• 【python之路30】反射及模块


    一、反射

    1、反射的基本介绍:

    反射是所有程序的专有名词,在java,C#语言中都存在反射,那么什么是反射呢?

    python中 的反射概括来说:是通过字符串的形式导入模块,并通过字符串的形式去模块中寻找函数并执行。

    总结:

    可以以字符串的形式去(某个)对象操作的成员。模块实际也是一个对象。

    2、利用字符串来导入模块

    根据用户输入的模块名(input)来导入模块:

    详见:24-9

    # 模块  *****
    # 什么是模块
    # 为什么要有模块
    # 模块分为哪几种
    # import
        # import 的时候发生了什么
        # 在import的时候命名空间的变换
        # 重命名
        # 一行导入多个模块
    # from ... import ...
        # from import 的时候发生了什么
        # 在import的时候命名空间的变换
        # 重命名 as
        # 一行导入多个名字
        # from 模块 import *
        # * 和 __all__ 的相关性
    # 模块相关的其他知识
        # 1.把模块当成脚本运行  : 从本模块中反射本模块中的变量
        # if __name__ == '__main__':
        #     所有不需要调用就能执行的内容
        # import sys
        # getattr(sys.modules[__name__],'要反射的变量名')
        # 2.模块搜索路径   sys.path
        # 3.pyc编译文件
        # 4.重新加载模块 已经导入的模块即便被修改在程序执行过程中也不会生效
        # 5.模块的循环引用 - 不允许
    # 包 ***
    # 什么是包? 集合了一组py文件 提供了一组复杂功能的
    # 为什么要有包?  当提供的功能比较复杂,一个py文件写不下的时候
    # 包中都有什么?  至少拥有一个__init__.py
    # 直接导入模块
        # import 包.包.模块
        # 包.包.模块.变量
        # from 包.包 import 模块  # 推荐 平时写作业的过程
        # 模块.变量
    # 导入包 读框架源码的时候
        # 如要希望导入包之后 模块能够正常的使用 那么需要自己去完成init文件的开发
        # 包中模块的 绝对导入
        # 包中模块的 相对导入
            # 使用了相对导入的模块只能被当做模块执行
            # 不能被当做脚本执行

    #!usr/bin/env python
    # -*- coding:utf-8 -*-
    
    modue = input('请输入您要导入的模块名:')
    RE = __import__(modue)  #相当于执行:import re as RE,__import__()把字符串形式的模块名作为模块导入

    如果导入的模块是多级目录的情况:

    #!usr/bin/env python
    # -*- coding:utf-8 -*-
    
    # from day14.lib import command
    # import day14.lib.command
    
    COM = __import__('day14.lib.command',fromlist=True)
    re = COM.f1()
    print(re)  #f1

     3、利用字符串来执行函数

    用户输入要导入的模块,和执行的函数:

    func = getattr(模块名,字符串格式的函数名),将字符串格式的函数指向func

    index.py中输入代码:

    #!usr/bin/env python
    # -*- coding:utf-8 -*-
    module = input('请输入您要导入的模块:')
    MD = __import__(module)   #如果用户输入command相当于:import command as MD
    func = input('请输入您要执行的函数:')
    FC = getattr(MD,func)  #如果用户输入f1相当于:创建了变量FC指向MD对象的f1函数
    re = FC() #相当于执行f1()
    print(re) #打印输出F1

     5、反射相关函数:

    1)getattr(模块,字符串变量名或函数名,默认值)

    #!usr/bin/env python
    # -*- coding:utf-8 -*-
    import command
    f = getattr(command,'f1',None) #当字符串函数不存在时不会报错返回None
    func = f()
    print(func)

    2)hasattr(模块,字符串变量名或函数名)   #判断是否存在字符串函数名

    #!usr/bin/env python
    # -*- coding:utf-8 -*-
    import command
    bol = hasattr(command,'f1')
    print(bol)  #True

    3)setattr(模块,字符串变量名或函数名,设置的值)

    seattr在内存中设置变量:

    #!usr/bin/env python
    # -*- coding:utf-8 -*-
    import command
    b1 = hasattr(command,'AGE')
    print(b1) #False,因为不存在AGE
    #当import command执行时会将command.py中的所有东西加载到内存,执行下面这句,是再在
    #内存中增加一个变量AGE=18
    setattr(command,'AGE',18)  #给command设置一个变量AGE = 18
    print(command.AGE) #18
    b2 = hasattr(command,'AGE')
    print(b2) #True,setattr增加了一个变量AGE

     setattr在内存中设置函数:

    #!usr/bin/env python
    # -*- coding:utf-8 -*-
    import command
    b1 = hasattr(command,'fn')
    print(b1) #False,因为不存在函数fn
    #当import command执行时会将command.py中的所有东西加载到内存,执行下面这句,是再在
    #内存中增加一个函数fn
    setattr(command,'fn',lambda a:a+1)  #给command设置一个函数fn(a):return a+1
    b2 = hasattr(command,'fn')
    print(b2) #True,setattr增加了一个函数fn

    4)delattr(模块,字符串函数名或变量)  #删除内存中的函数名或变量

    5)总结反射的定义:

    以字符串的形式去某个模块中需找东西(getattr)

    以字符串的形式去某个模块中判断东西是否存在(hasattr)

    以字符串的形式去某个模块中设置东西(setattr)

    以字符串的形式去某个模块删除东西(delattr)

    总结:以字符串中的形式去对象或模块中去操作成员(getattr、haattr、setattr、delattr)

    二、基于反射模拟WEB框架的路由系统

    lib文件夹下面的account.py文件中的代码为:

    #!usr/bin/env python
    # -*- coding:utf-8 -*-
    def login():
        print('login')
    def outlogin():
        print('outlogin')
    def nb():
        print('特别牛逼的功能')

    和lib同目录下index.py中的文件中的代码为:

    #!usr/bin/env python
    # -*- coding:utf-8 -*-
    from lib import account
    url = input('请输入URL:')
    if url.endswith('login'):
        account.login()
    elif url.endswith('outlogin'):
        account.outlogin()
    elif url.endswith('nb'):
        account.nb()
    else:
        print('404找不到页面')

    用下面的代码替换上面的代码后,account中无论增加多少函数,下面的代码不用改变就可以直接执行account文件中的函数了:

    #!usr/bin/env python
    # -*- coding:utf-8 -*-
    from lib import account
    url = input('请输入URL:')
    func_name = url.split('/')[-1]  #分隔网址,得到最后一个/后的字符串
    if hasattr(account,func_name):
        target_func = getattr(account,func_name)  #得到目标函数
        target_func()
    else:
        print('404')

    可以继续优化,如果lib文件下面有很多py文件,那么导入模块也可以设置为动态的,例如:www.bai.com/account/login,可以动态的导入account

    代码如下:

    #!usr/bin/env python
    # -*- coding:utf-8 -*-
    
    url = input('请输入URL:')
    fun_module,func_name = url.split('/')[-2],url.split('/')[-1] #分隔网址,得到/后最后两个字符串
    target_module = __import__('lib.' + fun_module,fromlist=True)  #导入lib下字符串相应的模块
    if hasattr(target_module,func_name):
        target_func = getattr(target_module,func_name)  #得到目标函数
        target_func()
    else:
        print('404')

     三、反射在对象中使用

    class Foo:
        def __init__(self,name):
            self.name = name
        def f1(self):
            print("F1")
    foo =Foo('sun') #True
    re = hasattr(Foo,'f1') #True
    re1 = hasattr(foo,'f1') #True
    re2 = hasattr(foo,'name') #True
    print(re,re1,re2)
    #总结:hasattr在类中可以找到方法名;
    #在对象中可以找到变量和方法名;因为变量是存放在对象中的,而为什么能找到方法名呢,就是反射函数可以找到对象指针指向的类中找方法

     利用反射导入有类文件,并执行文件类中的方法

    #!usr/bin/env python
    # -*- coding:utf-8 -*-
    
    class Foo:
        def __init__(self,name):
            self.name = name
        def f1(self):
            print("F1")
    s文件中的代码
    #!usr/bin/env python
    # -*- coding:utf-8 -*-
    ss = __import__('s')
    s_class = getattr(ss,'Foo')    #获得类
    s_class_object = s_class('sun') #创建对象,相当于zoo = ss.Foo('sun')
    s_class_object_name = getattr(s_class_object,'name') #获得对象中的name字段
    s_class_object_f1 = getattr(s_class_object,'f1') #获得对象中的f1函数
    s_class_object_f1() #执行获得的函数
    print(s_class_object_name)
    反射引入s中的类及对象

    补充:

    从某个指定的命名空间中,用字符串数据类型的变量来获取变量值

    类名反射:静态属性    类方法   静态方法

    对象反射:对象属性   对象方法

    模块 : 模块中的方法

    自己模块中的调用方法:

    import sys

    mymodule = sys.modules['__main__']

    getattr(mymodule,变量名)

    import sys
    
    def login():
        print('mymodule login')
    
    
    print(__name__)  #在本文件中调用打印“__main__”,在其他文件中调用打印“mymodule”
    print(sys.modules[__name__])  ##sys.modules存放的是所有模块的名字和内存地址字典的键值对
    
    #不管是在本文件中调用还是在其他文件中调用都会成功
    #如果“getattr(sys.modules["__main__"],"login")()  ”在其他文件中调用会找不到login
    getattr(sys.modules[__name__],"login")()  

    from mymodule import *

    mymodule中如果定义: __all__ = ['name', 'login'],则在其他文件中利用*导入时,只能用name 和 login

  • 相关阅读:
    ojdbc15-10.2.0.4.0.jar maven 引用报错 Dependency 'com.oracle:ojdbc15:10.2.0.4.0' not found
    两个js文件之间函数互调问题
    Win10连接远程桌面时提示“您的凭据不工作”
    Examples_08_03
    ant打包命令
    SVN版本日志对话框命令使用指南
    activiti_SpringEnvironment
    shell脚本
    python爬虫
    php正则表达式总结
  • 原文地址:https://www.cnblogs.com/sunshuhai/p/6502004.html
Copyright © 2020-2023  润新知