Python “反射” 剖析详解

反射

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

模块中的反射
实例解析

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()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值