🍖反射

引入

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
'''
  • 问题 : 当我们的模块文件路径与执行文件不在同一级, 如下图所示

image-20201230084511126

🍔文件 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
'''
  • 当我们的模块文件路径与执行文件不在同一级, 如下图所示

image-20201230085553714

🍔文件 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
posted @ 2020-12-28 19:24  给你骨质唱疏松  阅读(159)  评论(0编辑  收藏  举报