一、反射的定义

反射是根据命名空间,通过变量名或者方法名的字符串形式来判断、查找、修改或者删除对应的值或者返回值
格式为:has/get/set/delattr(命名空间, 字符串形式的变量名/方法名)

  1. '''案例类'''
  2. class Student:
  3. ROLE = 'student'
  4. @classmethod
  5. def check_course(cls):
  6. print("检查课程")
  7. def choose_course(self):
  8. print("选择课程")
  9. def choosed_course(self):
  10. print("以选择的课程")
  11. @staticmethod
  12. def login(username, password):
  13. if username == '李镇' and password == '1234':
  14. print('登陆成功')
  15. else:
  16. print('登陆失败')
  17. class Level(Student):
  18. pass

二、判断变量或者方法是否存在

  • 判断是否存在
  1. i = 0
  2. while i <= 3:
  3. name = input('请输入方法名:')
  4. # getattr(Level, name)()
  5. # 这里获取的是方法,所以需要加上括号
  6. if hasattr(Student, name):
  7. # hasattr()判断的是是否存在属性/方法
  8. getattr(Student, name)()
  9. # 如果存在则获取
  10. else:
  11. print('方法名不存在')
  12. i += 1

三、获取对应的值或者返回值

  • 反射查看属性对应的值
  1. '''反射查看属性'''
  2. print(getattr(Student, 'ROLE'))
  3. print(getattr(Level, 'ROLE'))
  4. print(getattr(s1, 'ROLE'))
  5. # getattr()第一个参数是命名空间, 第二个参数是命名空间中变量名的字符串格式名称,获取的是变量名对应的值
  6. # 命名空间可以是类、实例化对象等
  • 反射查看方法对应的返回值
  1. '''反射查看方法'''
  2. getattr(Student, 'check_course')()
  3. # 反射调用类方法
  4. # print(getattr(Student, 'choose_course')())
  5. getattr(Student, 'login')('李镇', '1234')
  6. # 反射调用静态方法
  7. # getattr()第一个参数是命名空间, 第二个参数是命名空间中方法名的字符串格式名称,获取的是方法对应的返回值
  8. # getattr(Student, 'login')获取的只是方法对应的内存地址,需要加上括号来执行获取返回值

Note 需要注意的是:命名空间可以是类,也可以是实例化后的对象,当getattr()查看方法的时候获取到的是对应的内存空间,需要加上()才能获取到值 也可以通过子类来调用父类中的方法或者变量

  • 模块中反射的使用
  • 模块本质上还是有类组成的py文件或者类,所以模块的名即为命名空间,字符串为方法名

    • python模块反射案例(必须先导入使用的模块)

      1. '''模块的反射'''
      2. # import os
      3. # getattr(os, 'rename')('t2.py', 't1.py')
      4. # # 通过模块来修改文件的名称, rename本质上是一个函数
      5. i
      6. import os
      7. rename = os.rename
      8. # os.rename得到的是rename函数的内存地址
      9. result = getattr(os, 'rename')
      10. result = result('t2.py', 't1.py')
      11. '''模块的反射,命名空间是模块名,字符串是模块中的方法名'''
    • 把自己写的文件当作模块,反射模块中的函数,获取结果

      • 第一步:导入sys模块,获取py文件的内存地址,赋值给my_file变量,把内存地址作为getattr()的第一个参数
      • 第二步:在进行getattr(my_file,’文件中的函数’)()
  1. def func1():
  2. print("这是一个测试的程序")
  3. def func2():
  4. print("第二个程序")
  5. # func1()
  6. # func2()
  7. import sys
  8. # print(sys.modules)
  9. # print("-------------------")
  10. # print(sys.modules['__main__'])
  11. # # 这里打印的是py文件的内存地址(__main__)
  12. # my_file = sys.modules['__main__']
  13. # getattr(my_file, 'func1')()
  14. # # 把自己的py文件作为单独模块,反射文件中程序的结果
  15. my_file = sys.modules['__main__']
  16. # 先获取py文件的内存地址,这是固定格式
  17. getattr(my_file, 'func2')()
  18. # getattr()的第一个参数就是命名空间,也就是内存地址
  19. # 根据内存地址来调取函数,获取的是文件中函数的内存地址,在加括号执行函数返回结果

四、反射并修改内容

  • 修改内容的基本格式:setattr(命名空间, 变量名/方法名,替换内容)
  1. '''修改属性setattr()'''
  2. class A:
  3. def __init__(self, name):
  4. self.name = name
  5. a = A('李志')
  6. setattr(a, 'name', '刘欢')
  7. # setattr中的三个参数:第一个是命名空间/内存空间,第二个是需要被修改的变量/方法名,第三个是替换的内容
  8. print(a.name)
  9. # 结果是:刘欢

五、反射删除内容

  • 反射删除内容的基本格式:delattr(命名空间, 需要被删除的变量名/方法)
  1. class A:
  2. def __init__(self, name):
  3. self.name = name
  4. def func(self):
  5. print(self, '类的内存地址')
  6. '''删除属性delattr()'''
  7. delattr(a, 'name')
  8. # print(a.name)
  9. # 删除类中的属性
  10. delattr(a, 'func')
  11. getattr(a, 'func')()
  12. # 删除类中方法

六、综合利用反射,创建简单的教务管理系统

  1. import sys
  2. class Teacher:
  3. operate_list = [('打印教师信息', 'func'), ]
  4. # 把方法和提示信息作为元组组成一个方法信息列表
  5. def __init__(self, name):
  6. self.name = name
  7. def func(self):
  8. print('教师')
  9. @classmethod
  10. def status_attr(cls):
  11. return cls.operate_list
  12. class Student:
  13. operate_list = [('查看课程', 'check_course'), ('选择课程', 'choose_course')]
  14. def __init__(self, name):
  15. self.name = name
  16. def check_course(self):
  17. print('查看课程')
  18. def choose_course(self):
  19. print("请选择课程")
  20. @classmethod
  21. def status_attr(cls):
  22. return cls.operate_list
  23. class Manager:
  24. operate_list = [('创建学生信息','create_student'), ('创建课程', 'create_course'), ('查看学生信息', 'check_student_info')]
  25. def __init__(self, name):
  26. self.name = name
  27. def create_student(self):
  28. print('创建学生对象')
  29. def create_course(self):
  30. print('创建课程')
  31. def check_student_info(self):
  32. print('核对学生对象')
  33. @classmethod
  34. def status_attr(cls):
  35. return cls.operate_list
  36. def login():
  37. with open('userinfo.txt', mode='r', encoding='utf-8') as f:
  38. username = input('请输入用户名:')
  39. password = input('请输入密码:')
  40. # 提示用户输入用户名和密码
  41. for line in f:
  42. line = line.rstrip('\n')
  43. user, pwd, ident = line.split('|')
  44. # 从文件中读取用户名、密码和身份信息
  45. if user == username and pwd == password:
  46. print("登陆成功")
  47. return username, ident
  48. # 判断信息是否一致,一致返回用户名和身份
  49. # 程序的入口
  50. import sys
  51. def main():
  52. global i
  53. file = sys.modules['__main__']
  54. # 获取文件所在的内存空间地址,需要用sys模块
  55. user, ident = login()
  56. # 接收信息
  57. if hasattr(file, ident):
  58. # 注意:文件中的ident和程序中的类名是完全一样的,所以可以把ident作为类名来使用
  59. obj = getattr(file, ident)(user)
  60. # 获取类的内存地址,并实例化对象
  61. print(obj.name)
  62. operate_list = getattr(obj, 'operate_list')
  63. # 反射类中的静态字段
  64. for num, i in enumerate(operate_list, 1):
  65. print(num, i[0])
  66. # 利用枚举获取静态字段信息,序号和信息共同组成,enuerate(可迭代对象, 设定的第一个序号值)
  67. while True:
  68. num = int(input("请输入选项序号:"))
  69. choose_name = operate_list[num - 1][1]
  70. # 提示用户输入需要,根据序号找到方法对应的提示信息
  71. getattr(obj, choose_name)()
  72. # 再根据信息找到对应的方法,并反射内容
  73. # getattr(operate_list, i[num - 1])
  74. main()