date: 2021-06-25title: python中的反射 #标题
tags: Python #标签
categories: python # 分类

记录下python中反射的概念,参考:

  • 老男孩教育
  • python中的反射

    什么是反射?
    1、有时我们要访问某个变量或是方法时并不知道到底有没有这个变量或方法,所以就要做些判断。判断是否存在字符串对应的变量及方法。
    2、我们知道访问变量时是不能加引号的,否则会被当成字符串处理。如果要通过字符串找到对应的变量,那该怎么办呢

反射就是用于解决上面两个问题而产生的,所谓反射,按我的理解就是反过来告诉我字符串是什么,是变量or方法。

python的反射,它的核心本质其实就是利用字符串的形式去对象(模块)中操作(查找/获取/删除/添加)成员,一种基于字符串的事件驱动!

反射就是用字符串数据类型的变量名去访问变量。

这里来个最基本的反射用法,假设有两个文件,demo_01.py和demo_02.py,demo_02.py文件内容如下:

  1. name='吕建钊'

demo_01.py文件内容如下:

  1. name = 'alex'
  2. import demo_02 # 导入demo_02.py文件
  3. module = demo_02 # 指定要操作哪个模块中的变量
  4. a = input('>>> ').strip() # 输入变量名称
  5. print(getattr(module, a))
  6. # 执行结果如下:
  7. >>> name # 输入name,输出的是demo_02.py中的值
  8. 吕建钊
  9. # 现在将demo_01.py文件改为如下内容:
  10. name = 'alex'
  11. import demo_02
  12. import sys
  13. module = sys.modules['__main__'] # 获取当前脚本的绝对路径
  14. a = input('>>> ').strip() # 输入变量名称
  15. print(getattr(module, a))
  16. # 再次执行,name的值就成了当前脚本中的值
  17. >>> name
  18. alex

通过以上方法,可以知道了反射最基本的用法,就是可以通过字符串类型的数据去操作相应的变量或者方法等。

python中访问类或多项的成员有三种方式:

# 如下所示 obj 为对象 var为变量 func为函数
obj.var 或 obj.func()
obj.__dict__['var']
getattr(obj,'var')

反射当前模块,还可以导入其他模块,利用反射查找该模块是否存在某种方法:

import sys


def s1():
    print('s1')


def s2():
    print('s2')


this_module = sys.modules['__main__']  # __main__表示当前文件
print(hasattr(this_module, 's1'))  # 判断's1'是否在当前文件的方法名中
getattr(this_module, 's2')()  # 执行s2函数


# 打印结果如下:
True
s2

import和import()

  • import:导入/引入一个python标准模块,其中包括.py文件、带有init.py文件的目录;
  • import作用:同import语句同样的功能,但import是一个函数,并且只接收字符串作为参数

函数功能用于动态的导入模块,主要用于反射或者延迟加载模块。
__import__(module)相当于import module
__import__(package.module)相当于from package import name,如果fromlist不传入值,则返回package对应的模块,如果fromlist传入值,则返回package.module对应的模块。
我动态输入一个模块名,可以随时访问到导入模块中的方法或者变量。


imp = input("请输入模块:")
dd = __import__(imp)  # 等价于import imp
inp_func = input("请输入要执行的函数:").strip()
f = getattr(dd, inp_func, None)
# 作用:从导入模块中找到你需要调用的函数inp_func,然后
# 返回一个该函数的引用.没有找到就返会None

f()  # 执行该函数

上面还存在一点点小问题:那就是我的模块名有可能不是在本级目录中存放着:

dd = __import__("lib.text.commons")  #这样仅仅导入了lib模块
dd = __import__("lib.text.commons",fromlist = True)  #改用这种方式就能导入成功

inp_func = input("请输入要执行的函数:")
f = getattr(dd,inp_func)
f()

反射的核心方法

# getattr()函数是Python自省的核心函数,具体使用大体如下:
class A:
    def __init__(self):
        self.name = 'zhangjing'
        self.age = '24'

    def method(self):
        print('method print')


Instance = A()
print(getattr(Instance, 'name', 'not find'))  # 如果Instance 对象中有属性name则打印self.name的值,否则打印'not find'
print(getattr(Instance, 'age', 'not find'))  # 如果Instance 对象中有属性age则打印self.age的值,否则打印'not find'
print(getattr(Instance, 'method', 'default'))  # 如果有方法method,否则打印其地址,否则打印default
print(getattr(Instance, 'method')())  # 如果有方法method,否则打印其地址,否则打印default

# hasattr
print(hasattr(Instance, 'method'))  # 判断对象中是否存在名字为method的属性或方法

# 说明:判断对象object是否包含名为name的特性(hasattr是通过调用getattr(ojbect, name)
# 是否抛出异常来实现的)

# setattr
setattr(Instance, 'hobby', 'running')
# 这是相对应的getattr()。参数是一个对象, 一个字符串和一个任意值。字符串可能会列出一个现有的属性或一个新的属性。
# 这个函数将值赋给属性的。该对象允许它提供。例如, setattr(x,“foobar”, 123)相当于x.foobar = 123。

# delattr
print(hasattr(Instance, 'hobby'))
delattr(Instance, 'hobby')
# 与setattr() 相关的一组函数。参数是由一个对象(记住python中一切皆是对象)
# 和一个字符串组成的。string参数必须是对象属性名之一。该函数删除该obj的一个由string指定的属性。
# delattr(Instance, 'hobby') 等同于 del Instance.hobby

print(hasattr(Instance, 'hobby'))