阅读目录
反射
根据字符串的形式去某个对象中 操作 他的成员
模块中的反射
实例解析
reflex包下
handler.py
f0 = 9
def f1():
print('F1')
def f2():
print('F2')
def f3():
print('F3')
def f4():
print('F4')
def f5():
print('F5')
run.py
from types import FunctionType
import handler
print(dir(test12)) # ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__',
'__package__', '__spec__', 'f0', 'f1', 'f2', 'f3', 'f4', 'f5']
while True:
print("""
系统支持的函数有:
0. f0
1. f1
2. f2
3. f3
4. f4
5. f5
""")
val = input("请输入要执行的函数:") # val = "f1"
# 错误 handler.val()
if hasattr(handler, val): # 判断 handler模块是否存在 某个属性
func_or_val = getattr(handler, val) # 拿到相应的成员
# 根据字符串为参数,去模块(对象)中寻找与之同名的成员。
if isinstance(func_or_val, FunctionType): # 判断是否是函数
func_or_val() # 是函数就直接 函数名()执行
else:
print(func_or_val) # 是变量
else:
print('handler中不存在输入的属性名')
# 比较以前执行的方法:
if val == 'f1':
handler.f1()
elif val == 'f2':
handler.f2()
elif val == 'f3':
handler.f3()
elif val == 'f4':
handler.f4()
elif val == 'f5':
handler.f5()
面向对象中的反射:
实例解析
class Foo(object):
country = "中国"
def func(self):
print("hello new world")
# 第一种是:判断是否是类中的函数,函数是加括号运行的,需要主动传参(对象),
# --可以是类,也可以是对象的
v = getattr(Foo,'func') # Foo.func 根据字符串为参数,去类中寻找与之同名的成员。
print(v) # <function Foo.func at 0x000002688AD5A950>
# 要使用v() 需要先实例对象,将对象传入参数
obj1=Foo()
v(obj1) #hello new world
v(object) #hello new world
# 第二种是:判断是否是对象中的方法
obj = Foo()
v = getattr(obj,"func") # obj.func # 根据字符串为参数,去对象中寻找与之同名的成员。
print(v) #<bound method Foo.func of <__main__.Foo object at 0x0000029B6C509208>>
v() # 这种方式参数会自动传递
练习
class Account(object):
func_list = ['login', 'logout', 'register']
def login(self):
"""
登录
:return:
"""
print('登录111')
def logout(self):
"""
注销
:return:
"""
print('注销111')
def register(self):
"""
注册
:return:
"""
print('注册111')
def run(self): # 主程序
print("""
请输入要执行的功能:
1. 登录
2. 注销
3. 注册
""")
choice = int(input('请输入要执行的序号:'))
func_name = Account.func_list[choice - 1]
# 去类中找
func = getattr(Account, func_name) # 相当于Account.login
func(self) # 是函数,需要自己传参,也可以传已经实例的对象,如obj1
# 去对象中找(最佳)
func = getattr(self, func_name) # 相当于self.login
func() # 是对象,可以直接self.函数名()
obj1 = Account()
obj1.run()
obj2 = Account()
obj2.run()
拓展题
handly.py
import handly
def func():
objs = []
name_list = dir(test12)
# print(name_list) ['Base', 'F1', 'F2', 'F3', 'F4', 'F5', 'F6',
# '__builtins__', '__cached__', '__doc__', '__file__', '__loader__',
# '__name__', '__package__', '__spec__']
base = getattr(test12, "Base") # 拿到"Base"
for el in name_list:
if not el.startswith('__') and issubclass(getattr(test12, el), base) and el != 'Base':
objs.append(getattr(test12, el)())
print(objs)
func()
反射的成员
getattr 根据字符串的形式,去对象中找成员。
hasattr 根据字符串的形式,去判断对象中是否有成员。
setattr 根据字符串的形式,去判断对象动态的设置一个成员(内存)
delattr 根据字符串的形式,去删除对象动态的设置一个成员(内存)
在模块中
xx.py
x1 = 123
def f1(arg):
print(arg,666)
reflex.py
import xx
# getattr
v1 = getattr(xx, 'x1')
v2 = getattr(xx, 'f1')
v2('杨森')
# hasattr
v3 = hasattr(xx, 'x1')
v4 = hasattr(xx, 'f1')
v4 = hasattr(xx, 'f1')
v5 = hasattr(xx, 'xxxxxxx')
print(v3, v4, v5)
# setattr
setattr(xx, 'x2', 999)
v6 = getattr(xx, 'x2')
print(v6)
setattr(xx, 'f2', lambda x: x + 1)
v7 = getattr(xx, 'f2')
v8 = v7(1)
print(v8)
# delattr
delattr(xx, 'x1')
v9 = getattr(xx, 'x1')
print(v9)
在类中
class Foo(object):
def __init__(self, a1):
self.a1 = a1
self.a2 = None
obj = Foo(1)
v1 = getattr(obj, 'a1')
print(v1)
setattr(obj, 'a2', 2)
v2 = getattr(obj, 'a2')
print(v2) # 尽量少用
反射运用场景
python一切皆对象
问题:有什么后面可以加()的?
函数()
类()
对象()
方法()
以上所有,都可以被调用。
def func():
pass
class Foo(object):
def __call__(self, *args, **kwargs):
pass
def func(self):
pass
obj = Foo()
print(callable(func))
print(callable(Foo))
print(callable(obj))
print(callable(obj.func)) # 方法()
importlib模块
根据字符串的形式导入模块
通常导入模块,“ import 模块 ”,这个 模块 是个变量名或者说是名称,它不是字符串
模块名 = importlib.import_module(‘模块名所在路径’)
例如:redis = importlib.import_module(‘utils.redis’)
importlib模块会去执行:from utils import redis
全部用字符串(反射)的形式完成
# redis.py
def func()
print("redis的func")
拓展示例
import importlib
middleware_classes = [
'utils.redis.Redis',
'utils.mysql.MySQL',
'utils.mongo.Mongo'
] # middeleware 中间件
for path in middleware_classes:
module_path, class_name = path.rsplit('.', maxsplit=1)
# resplit 从右边开始找到“.”切割,maxsplit=1只切割一次;
# 这样就把 模块名 和 类名 分割开了
module_object = importlib.import_module(module_path) # from utils import redis
cls = getattr(module_object, class_name) # 拿到一个类
obj = cls() # 对类进行实例化
obj.connect()
# 用字符串的形式导入模块。
# from utils import redis 通常模式导入模块(可以被反射替换)
redis = importlib.import_module('utils.redis')
# 用字符串的形式去对象(模块)找到他的成员。
# redis.func() 通常模式导入模块,执行成员(可以被反射替换)
getattr(redis, 'func')()
------
# radis.py
class Redis(object):
def connect(self):
print('连接redis')
Django源码就有用到
实战题:完成一个学生选课功能
不用反射实现
class Student:
def __init__(self, stu_no, stu_name):
self.stu_no = stu_no
self.stu_name = stu_name
self.cour_list = []
def watch_all(self): # 查看学生选课详情
for c in self.cour_list:
print(f'学生{self.stu_name}选了{c.cour_name}课')
def add_course(self, course): # 学生添加课程
self.cour_list.append(course)
class Course:
def __init__(self, cour_no, cour_name, teacher=None):
self.cour_no = cour_no
self.cour_name = cour_name
self.teacher = teacher # 设置了默认值参数,可传可不传,空间给开辟好了
def watch_all(self): # 查看所有课程信息
print(f'{self.cour_no},{self.cour_name},{self.teacher.tea_name}')
def add_teacher(self, teacher): # 设置老师,前面初始化可能为空
self.teacher = teacher
class Teacher:
def __init__(self, tea_no, tea_name, tea_phone):
self.tea_no = tea_no
self.tea_name = tea_name
self.tea_phone = tea_phone
from random import randint
c1 = Course("01", "语文")
c2 = Course("02", "数学")
c3 = Course("03", "地理")
c4 = Course("04", "英语")
c5 = Course("05", "物理")
c6 = Course("06", "历史")
t1 = Teacher('01', '唐伯虎', '123456')
t2 = Teacher('02', '祝枝山', '123156')
t3 = Teacher('03', '文征明', '123256')
t4 = Teacher('04', '李白', '123956')
t5 = Teacher('05', '皇冠', '123856')
t6 = Teacher('06', '谢静', '123756')
c1.add_teacher(t1)
c2.add_teacher(t2)
c3.add_teacher(t3)
c4.add_teacher(t4)
c5.add_teacher(t5)
c6.add_teacher(t6)
c_lst = [c1, c2, c3, c4, c5, c6] # 课程列表,里面课程包括基本信息和设置好的老师
stu_lst = []
for i in range(30):
stu = Student(i, '学生' + str(i))
s = set()
while len(s) < 3:
s.add(randint(0, 5)) # set的妙用,随机产生三个不重复的小数
for n in s:
# print(n)# 随机出来课程的索引
stu.add_course(c_lst[n])
stu_lst.append(stu)
for s in stu_lst:
print(f'学生编号:{s.stu_no},学生姓名:{s.stu_name}')
for c in s.cour_list:
print(f'\t选了:{c.cour_name},课程编号是:{c.cour_no},任课老师的电话是:{c.teacher.tea_phone}')
用反射来完成
class Course:
def __init__(self, name, price, period):
self.name = name
self.price = price
self.period = period
class Student:
func_list = [
{'text': '选课', 'name': 'select_course'},
{'text': '查看课程', 'name': 'show_selected_course'},
{'text': '删除课程', 'name': 'show_del_course'},
]
def __init__(self, name):
self.name = name
self.courses = []
def select_course(self, csl):
for i, item in enumerate(csl, 1):
print(i, item.name, item.price, item.period)
while True:
num = int(input('请选择你要选择的课程序号')) # 如果选了1 >>> xx-1 90 90
num = num - 1
course_obj = csl[num] # total_course_list[0] >>> 拿到第一个课程对象?
if course_obj not in self.courses:
self.courses.append(course_obj)
if len(self.courses) == len(csl):
print("你已选择完所有课程")
return
def show_selected_course(self, csl):
for choice_course in self.courses:
print(choice_course.name, choice_course.price, choice_course.period)
def show_del_course(self, csl):
num = int(input('请选择你要删除的课程序号'))
num = num - 1
self.courses.pop(num)
def run():
total_course_list = []
for i in range(1, 6):
obj = Course('xx-%s' % i, 90, 90)
total_course_list.append(obj)
stu_name = input('请输入学生姓名')
stu = Student(stu_name)
for i, item in enumerate(stu.func_list, 1):
print(i, item['text'])
while True:
num = int(input('请选择要执行的功能序号:'))
num = num - 1
row = stu.func_list[num]
name = row['name']
func = getattr(stu, name) # 用 对象名.函数名()来调用,不用考虑参数问题
func(total_course_list) # 对参数进行限制,给它一个默认参数,用不用就看它自己了
if __name__ == '__main__':
run()