反射

反射的定义

根据字符串的形式去某个对象中操作成员

  1. 根据字符串的形式去一个对象中寻找成员
  2. 根据字符串的形式去一个对象中设置成员
  3. 根据字符串的形式去一个对象中删除成员
  4. 根据字符串的形式去一个对象中判断成员是否存在

初始反射

通过字符串的形式,导入模块

根据用户输入的模块名称,导入对应的模块并执行模块中的方法

# python 版本
➜  python3 -V
Python 3.5.3
# commons.py为模块文件
➜  ls commons.py reflection.py
commons.py    reflection.py

# commons.py文件内容
➜  cat commons.py
#!/usr/bin/env python
# _*_ coding:utf-8 _*_

def f1():
    return "F1"
def f2():
    return "F2"

# reflection.py文件内容
➜  cat reflection.py
#!/usr/bin/env python
# _*_ coding:utf-8 _*_

mod_name = input("请输入模块名称>>>")
print(mod_name,type(mod_name))
# 通过__import__的方式导入模块,并赋值给dd
dd = __import__(mod_name)
# 执行f1()函数
ret = dd.f1()
print(ret)

# 执行
➜  python3 reflection.py
请输入模块名称>>>commons
commons <class 'str'>
F1

通过字符串的形式,去模块中寻找指定函数,并执行

用户输入模块名称和函数名称,执行指定模块内的函数or方法

➜  python_test cat commons.py
#!/usr/bin/env python
# _*_ coding:utf-8 _*_

def f1():
    return "F1"
def f2():
    return "F2"

➜  python_test cat reflection-2.py
#!/usr/bin/env python
# _*_ coding:utf-8 _*_

# 输入模块名
mod_name = input("请输入模块名称>>>")
# 输入函数名
func_name = input("请输入函数名>>>")

# 导入模块
dd = __import__(mod_name)
# 导入模块中的方法
target_func = getattr(dd,func_name)
# 查看target_func和dd.f1的内存地址
print(id(target_func),id(dd.f1))
# 执行 target_func()函数
result = target_func()

# 输出结果
print(result)
➜  python_test python3 reflection-2.py
请输入模块名称>>>commons
请输入函数名>>>f1
# 返回内存地址
4315949528 4315949528
F1

反射相关的函数

getattr(object,name[,default])

根据字符串的形式去一个对象中寻找成员

➜  python_test cat commons.py
#!/usr/bin/env python
# _*_ coding:utf-8 _*_

name = "yang"

def f1():
    return "F1"
def f2():
    return "F2"
>>> import commons
>>> getattr(commons,"f1")
<function f1 at 0x101c80ea0>
>>> getattr(commons,"f1f")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'commons' has no attribute 'f1f'

执行获取到的函数

>>> target_func = getattr(commons,"f1")
>>> target_func
<function f1 at 0x101c80ea0>
>>> target_func()
'F1'

通过设置默认值可以避免获取不到方法时报错

# 设置一个默认值为None
>>> target_func = getattr(commons,"f1f",None)
>>> target_func
>>>

通过getattr获取模块中的全局变量

>>> import commons
>>> getattr(commons,"name",None)
'yang'

setattr(object,name,value)

根据字符串的形式去一个对象中设置成员

设置全局变量

>>> getattr(commons,"age",None)
>>> setattr(commons,"age",18)
>>> getattr(commons,"age",None)
18

setattr结合lambda表达式设置一个函数

>>> setattr(commons,"as",lambda : print("as"))
>>> getattr(commons,"as")
<function <lambda> at 0x101c80e18>
>>> aa = getattr(commons,"as")
>>> aa()
as

delattr(object,name)

根据字符串的形式去一个对象中删除成员

>>> getattr(commons,"name")
'yang'
>>> delattr(commons,"name")
>>> getattr(commons,"name")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'commons' has no attribute 'name'
>>>

hasattr(object,name)

根据字符串的形式去一个对象中判断成员是否存在

>>> hasattr(commons,"name")
False
>>> setattr(commons,"name","yang")
>>> hasattr(commons,"name")
True

__import__方式导入多层模块

到common.py所在目录的上一层目录,执行python

>>> m = __import__("python_test.commons")
>>> m
<module 'python_test' (namespace)>
>>> m = __import__("python_test.commons",fromlist=True)
>>> m
<module 'python_test.commons' from '/root/python_test/commons.py'>

基于反射模拟web框架路由系统

find_index.py文件内容

#!/usr/bin/env python
# _*_ coding:utf-8 _*_

url = input("请输入url: ")
target_module,target_func = url.split('/')

m = __import__("lib." + target_module,fromlist=True)

if hasattr(m,target_func):
    target_func = getattr(m,target_func)
    r = target_func()
    print(r)
else:
    print("404")

目录结构及文件内容

➜  tree .
.
├── find_index.py
└── lib
    ├── account.py
    └── commons.py

1 directory, 3 files

➜  cat lib/commons.py
#!/usr/bin/env python
# _*_ coding:utf-8 _*_

name = "yang"

def f1():
    return "F1"
def f2():
    return "F2"

➜  cat lib/account.py
#!/usr/bin/env python
# _*_ coding:utf-8 _*_

def login():
    return "login"
def logout():
    return "logout"

执行

➜  python3 find_index.py
请输入url: account/login
login
➜  python3 find_index.py
请输入url: account/logout
logout
➜  python3 find_index.py
请输入url: commons/f1
F1
➜  python3 find_index.py
请输入url: commons/f2
F2
➜  python3 find_index.py
请输入url: commons/fdas
404