zoukankan      html  css  js  c++  java
  • 面向对象-反射和元类

    一、反射(reflect)

    反射指的是一个对象应该具备可以检测,修改,增加自身属性的能力(反射其实就是对属性的增删改查),

    通过字符串操作属性

    涉及的四个函数,就是普通的内置函数

     2.判断属性

      hasattr 是否存在 

      getattr 取出属性 

      setattr 添加属性 

      delattr   删除属性

    hasattr  getattr setattr delattr 
    
    p = Person("jack",18,"man")
    
    # if hasattr(p,"name"): # 1.判断某个对象是否存在某个属性
    #     print(getattr(p,"names",None)) # 2.从对象中取出属性,第三个值位默认值 当属性不存在是返回默认值
    
    # 3.为对象添加新的属性
    setattr(p,"id","123")
    print(p.id)
    
    # 4.从对象中删除属性
    delattr(p,"id")
    print(p.id)
    View Code

      3.使用场景:

        框架设计方式

        框架代码

    """
    反射被称为框架的基石,为什么
    因为框架的设计者,不可能提前知道你的对象到底是怎么设计的
    所以你提供给框架的对象 必须通过判断验证之后才能正常使用
    判断验证就是反射要做的事情,
    当然通过__dict__也是可以实现的, 其实这些方法也就是对__dict__的操作进行了封装
    需求:要实现一个用于处理用户的终端指令的小框架
    框架就是已经实现了最基础的构架,就是所有项目都一样的部分
    """
    
    import plugins
    
    # 框架已经实现的部分
    def run(plugin):
        while True:
            cmd = input("请输入指令:")
            if cmd == "exit":
                break
            # 因为无法确定框架使用者是否传入正确的对象所以需要使用反射来检测
            # 判断对象是否具备处理这个指令的方法
            if hasattr(plugin,cmd):
                # 取出对应方法方法
                func = getattr(plugin,cmd)
                func() # 执行方法处理指令
            else:
                print("该指令不受支持...")
        print("see you la la!")
    
    
    # 创建一个插件对象 调用框架来使用它
    # wincmd = plugins.WinCMD()
    # 框架之外的部分就有自定义对象来完成
    linux = plugins.LinuxCMD()
    run(linux)
    框架代码

        插件部分

    class WinCMD:
    
        def cd(self):
            print("wincmd 切换目录....")
    
        def delete(self):
            print("wincmd 要不要删库跑路?")
    
        def dir(self):
            print("wincmd 列出所有文件....")
    
    class LinuxCMD:
    
        def cd(self):
            print("Linuxcmd 切换目录....")
    
        def rm(self):
            print("Linuxcmd 要不要删库跑路?")
    
        def ls(self):
            print("Linuxcmd 列出所有文件....")
    插件部分    

        上述框架代码中 写死了必须使用某个类,这是不合理的,因为无法提前知道对方的类在什么地方 以及类叫什么

        所以我们应该为框架的使用者提供一个配置文件,要求对方将累的信息写入配置文件

        然后框架自己去加载需要的模块

    import importlib
    import settings
    
    # 框架已经实现的部分
    def run(plugin):
        while True:
            cmd = input("请输入指令:")
            if cmd == "exit":
                break
            # 因为无法确定框架使用者是否传入正确的对象所以需要使用反射来检测
            # 判断对象是否具备处理这个指令的方法
            if hasattr(plugin,cmd):
                # 取出对应方法方法
                func = getattr(plugin,cmd)
                func() # 执行方法处理指令
            else:
                print("该指令不受支持...")
        print("see you la la!")
    
    
    # 创建一个插件对象 调用框架来使用它
    # wincmd = plugins.WinCMD()
    # 框架之外的部分就有自定义对象来完成
    
    # 框架 得根据配置文件拿到需要的类
    
    path = settings.CLASS_PATH
    # 从配置中单独拿出来 模块路径和 类名称
    module_path,class_name = path.rsplit(".",1)
    #拿到模块
    mk = importlib.import_module(module_path)
    # 拿到类
    cls = getattr(mk,class_name)
    # 实例化对象
    obj = cls()
    #调用框架
    run(obj)
    最后的框架代码:

        如此一来,框架就与实现代码彻底解耦了,只剩下配置文件

    二、元类 metaclass

     元类是什么,用于创建类的类,万物皆对象,类当然也是对象

    对象是通过类实例化产生的,如果类也是对象的话,必然类对象也是有另一个类实例化产生的

    默认情况下所有类的元类都是type

      验证:

    class Person:
        pass
    p = Person()
    
    print(type(p))
    print(type(Person))
    
    Person类是通过type类实例化产生的 
    View Code

      1、元类的目的:对创建类对象做一些限制,通过初始化方法,覆盖其中init方法就能实现需求

      代码:

    """
    只要继承了type 那么这个类就变成了一个元类
    """
    # 定义了一个元类
    class MyType(type):
        def __init__(self,clss_name,bases,dict):
            super().__init__(clss_name,bases,dict)
            print(clss_name,bases,dict)
            if not clss_name.istitle():
                raise Exception("你丫的 类名不会写...")
    
    # 为pig类指定了元类为MyType
    class Pig(metaclass=MyType):
        pass
    
    class Duck(metaclass=MyType):
        pass
    View Code

      2、元类中call方法

    当你调用类对象时会自动执行元类中的__call__方法 ,并将这个类本身作为第一个参数传入,以及后面的一堆参数

    覆盖元类中的call之后,这个类就无法产生对象,必须调用super().__call__来完成对象的创建
    并返回其返回值

      场景:

        当你想要控制对象的创建过程时,就覆盖call方法

        当你想要控制类的创建过程时,就覆盖init方法

    class MyType(type):
        def __call__(self, *args, **kwargs):
            new_args = []
            for a in args:
                new_args.append(a.upper())
    
            print(new_args)
            print(kwargs)
            return super().__call__(*new_args,**kwargs)
    
    
    class Person(metaclass=MyType):
        def __init__(self,name,gender):
            self.name = name
            self.gender = gender
    
    p = Person("jack",gender="woman")
    print(p.name)
    print(p.gender)
    View Code

    注意:一旦覆盖了call必须调用父类的call方法来产生对象并返回这个对象

       3、new方法

    当要创建类对象时,会首先执行元类中的__new__方法,拿到一个空对象,然后会自动调用__init__来对这个类进行初始化操作
    注意:,如果你覆盖了该方法则必须保证,new方法必须有返回值且必须是,对应的类对象     

    class Meta(type):
    
        def __new__(cls, *args, **kwargs):
            print(cls) # 元类自己
            print(args) # 创建类需要的几个参数  类名,基类,名称空间
            print(kwargs) #空的 
            print("new run")
            # return super().__new__(cls,*args,**kwargs)
            obj = type.__new__(cls,*args,**kwargs)
            return obj
        
        def __init__(self,a,b,c):
            super().__init__(a,b,c)
            print("init run")
    class A(metaclass=Meta):
        pass
    print(A)
    
    #总结new方法和init 都可以实现控制类的创建过程,init更简单
    View Code

      4、单例设计模式

      单例:指的是一个类产生一个对象

        为什么要使用单例:单例是为了节省 资源,当一个类的所有对象属性全部相同时,则没有必要创建多个对象

    # 单例n元类
    class Single(type):
        def __call__(self, *args, **kwargs):
            if hasattr(self,"obj"): #判断是否存在已经有的对象
                return getattr(self,"obj") # 有就返回
    
            obj = super().__call__(*args,**kwargs) # 没有则创建
            print("new 了")
            self.obj = obj # 并存入类中
            return obj
    
    
    class Student(metaclass=Single):
        def __init__(self,name):
            self.name = name
    
    
    class Person(metaclass=Single):
        pass
    
    # 只会创建一个对象
    Person()
    Person()
    View Code
  • 相关阅读:
    CSS3笔记!
    Charles与Jmeter结合编写接口测试
    Charles测试点集锦
    pom文件报错关于maven-compiler-plugin:3.1
    Jmeter获取数据库值并作为参数请求(转载)
    mysql基础操作语言
    JMeter之Ramp-up Period(in seconds)说明
    Jmeter查看QPS和响应时间随着时间的变化曲线(转载)
    Charles篡改后台数据
    接口测试之——Charles抓包及常见问题解决(转载自https://www.jianshu.com/p/831c0114179f)
  • 原文地址:https://www.cnblogs.com/xiaowangba9494/p/11272047.html
Copyright © 2011-2022 走看看