🍖反射
引入
1.什么 反射
- 反射就是通过字符串来操作类或者对象的属性
- Python中一切皆对象, 即都可以使用反射
2.反射的四个内置函数
反射的本质就是在使用内置函数, 其中反射有以下四个内置函数
-
hasattr : 判断一个属性或方法是否存在这个类中, 返回bool值
-
getattr : 获取属性值或者获取方法变量的内存地址 (根据字符串去获取obj对象里的对应的方法的内存地址 (加括号"()"就可以调用))
-
setattr : 给类或对象设置属性或方法 (通过setattr将外部的一个属性或函数绑定到实例中)
-
delattr : 删除类或对象的属性和方法
ps : getattr, hasattr, setattr, delattr对模块的修改都在内存中进行,并不会影响文件中真实内容
一.反射模块
1.模块导入到模块动态导入
- 之前我们使用模块都是使用 import 的方式进行导入
🍔文件 test24.py
def Foo1():
print("i am test24_Foo1")
def Foo2():
print("i am test24_Foo2")
def Foo3():
print("i am test24_Foo3")
🍔文件 test反射.py
import test24
test24.Foo1() # i am test24_Foo1
test24.Foo2() # i am test24_Foo2
test24.Foo3() # i am test24_Foo3
- 通过反射模块的方式动态的导入模块, Python提供了
__import__
来实现这一功能
🍔文件 test反射.py
Inp = input("请输入想要导入的模块>>").strip()
mode = __import__(Inp) # 通过字符串的方式导入你想要的导的模块
mode.Foo1()
mode.Foo2()
mode.Foo3()
'''输出
请输入想要导入的模块>>test24
i am test24_Foo1
i am test24_Foo2
i am test24_Foo3
'''
- 问题 : 当我们的模块文件路径与执行文件不在同一级, 如下图所示
🍔文件 setting.py
def Foo1():
print("i am setting_Foo1")
🍔文件 test反射.py
Inp = input("请输入想要导入的模块>>").strip()
mode = __import__(Inp)
mode.Foo1()
'''输出
请输入想要导入的模块>>conf.setting
抛出异常 "AttributeError: module 'conf' has no attribute 'Foo1'"
'''
- 解决方法
Inp = input("请输入想要导入的模块>>").strip()
mode = __import__(Inp,fromlist=True) # 添加参数
mode.Foo1()
'''输出
请输入想要导入的模块>>conf.setting
i am setting_Foo1
'''
2.第二种反射模块方法
- 使用
importlib
模块实现 (在Python3 框架中使用的比较多)
🍔文件 test26.py
def test():
print("i am test26")
🍔文件 test反射模块.py
import importlib
Inp = input("请输入你要导入的模块>>").strip()
mode = importlib.import_module(Inp)
mode.test()
'''输出
请输入你要导入的模块>>test26
i am test26
'''
- 当我们的模块文件路径与执行文件不在同一级, 如下图所示
🍔文件 setting.py
def Foo1():
print("i am setting_Foo1")
import importlib
Inp = input("请输入你要导入的模块>>").strip()
mode = importlib.import_module(Inp)
mode.Foo1()
'''输出
请输入你要导入的模块>>conf.setting
i am setting_Foo1
'''
二.hasattr( obj,name)
- hasattr : 判断一个属性或方法是否存在这个类中, 返回bool值
- hasattr是通过调用
getattr(ojbect, name)
是否抛出异常来实现的
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
def fight(self):
print("正在fight")
P1 = Person("小王",18)
print(hasattr(P1,"name")) # True
print(hasattr(P1,"age")) # True
print(hasattr(P1,"fight")) # True
print(hasattr(P1,"aaaa")) # False
三.getattr(obj,name)
- getattr : 获取属性值或者获取方法变量的内存地址
- 根据字符串去获取obj对象里的对应的方法的内存地址 (加括号"()"就可以调用)
🍔续上面模块动态导入补充
Inp = input("请输入想要导入的模块>>").strip()
mode = __import__(Inp,fromlist=True)
method = input("请输入你想要执行的方法>>").strip()
print(getattr(mode,method,None)) # 当在模块中没有找到该方法时,返回None, 找到时返回该方法的内存地址
'''🔰存在--输出
请输入想要导入的模块>>conf.setting
请输入你想要执行的方法>>Foo1
<function Foo1 at 0x00000214B2964438>
'''
'''🔰不存在---输出
请输入想要导入的模块>>conf.setting
请输入你想要执行的方法>>aaa
None
'''
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
def fight(self):
print("正在fight")
return 123
P1 = Person("小王",18)
print(getattr(P1,"name",None)) # 小王
print(getattr(P1,"aaaa",None)) # None
# 如果对象中有"name"属性,则打印"self.name"的值, 否则打印"None"
print(getattr(P1,"fight",None)) # [方法的内存地址]
# 如果对象中有"fight"方法, 返回该方法的内存地址, 否则返回"None"
print(getattr(P1,"fight",None)()) # 正在fight 123
# 加了括号,存在该方法的话运行该方法并返回该方法的返回值, 否则返回"None"
print("name" in P1.__dict__) # True
# 也可以通过"__dict__"来进行判断, 效果一样
四.setattr(obj,name,value)
- setattr : 给类或对象设置属性或方法
- 通过setattr将外部的一个属性或函数绑定到实例中
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
def fight(self):
print("正在fight")
return 123
P1 = Person("小王",18)
# 设置属性
setattr(P1,"sex","man")
print(getattr(P1,"sex",None)) # man
# 设置方法
def runrun(self):
print("正在跑")
setattr(Person,"run",runrun)
getattr(P1,"run",None)() # 正在跑
# 注意 : 绑定给对象的方法是放在类里面的, 如果使用反射, 则要反射到类的内部
# 如果要将方法反射给对象, 那么就需要手动传入对象 (不推荐这么使用)
def rrr(obj):
print(obj.name)
setattr(P1,"print_name",rrr)
P1.print_name(P1) # 小王
五.delattr(obj,name)
- delattr : 删除类或对象的属性和方法
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
def fight(self):
print("正在fight")
return 123
P1 = Person("小王",18)
delattr(P1,"name")
print(P1.__dict__) # {'age': 18}
getattr(P1,"name") # 报错 "AttributeError" 没有该属性
六.应用小示例
1.给空类动态设置属性
class Extend:
pass
E1 = Extend()
Inp1 = input("请输入你需要设置的属性名>>").strip()
Inp2 = input("请输入该属性对应的值>>").strip()
setattr(E1,Inp1,Inp2) # 设置属性
print(getattr(E1,Inp1,None))
'''输出
请输入你需要设置的属性名>>name
请输入该属性对应的值>>shawn
shawn
'''
2.反射的应用案例
class Person:
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
def print_name(self):
print(f"名字:{self.name}")
def print_age(self):
print(f"年龄:{self.age}")
def print_sex(self):
print(f"性别:{self.sex}")
def print_info(self):
print(self.name,self.age,self.sex)
def select(self):
while 1:
Inp = input("请输入需要选择的方法(q退出)>>").strip()
if Inp.lower() == "q":break
if hasattr(self,Inp): # 判断存不存在
getattr(self,Inp)() # 拿到内存地址加括号调用
else:
print("没有该功能")
P1 = Person("久人旅",23,"man")
P1.select()
'''输出
请输入需要选择的方法(q退出)>>aaaa
没有该功能
请输入需要选择的方法(q退出)>>print_name
名字:久人旅
请输入需要选择的方法(q退出)>>print_age
年龄:23
请输入需要选择的方法(q退出)>>print_sex
性别:man
请输入需要选择的方法(q退出)>>q
Process finished with exit code 0
'''
3.反射隐藏属性
- 前面类的封装那一篇我们已经介绍过了, 设置隐藏属性的时候会进行变形操作,下面我们与反射组合使用
class Person:
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.__sex = sex # 变形 "_Person__sex"
P1 = Person("闫帝喵",23,"man")
🍔通过反射获取隐藏属性
print(getattr(P1,"__sex")) # 报错 "AttributeError" 没有该属性
print(getattr(P1,"_Person__sex")) # man
🍔通过反射设置隐藏属性(为对象设置)
setattr(P1,"_Person__money",4654)
print(getattr(P1,"__money")) # 报错 "AttributeError" 没有该属性
print(getattr(P1,"_Person__money")) # 4654
🍔通过反射设置隐藏属性(为类设置)
setattr(Person,"_Person__money",13213)
print(getattr(P1,"__money")) # 报错 "AttributeError" 没有该属性
print(getattr(P1,"_Person__money")) # 13213