zoukankan      html  css  js  c++  java
  • d24 反射,元类

    反射

    反省,自省

    指一个对象应该具备可以检测,修改,添加自身属性的能力.

    四个函数:

    hasattr  判断某个对象是否在某个属性
    getattr  从对象中取出属性,第三个值为默认值,当属性不存在时,返回默认值
    setattr   为对象添加新的属性
    delattr      删除属性

    例子:

    class Person():
        def __init__(self,name,age,height):
            self.name = name
            self.age = age
            self.height = height
    
    p = Person('lala',16,158.5)
    # print(p.name)
    
    # print(hasattr(p,'name'))
    if hasattr(p,'name'):
        # 判断某个对象是否存在某个属性
        print(getattr(p,'name'))
        # 从对象中取出属性,第三个值位默认值 当属性不存在是返回默认值
        print(getattr(p,'age',None))
    
    #添加新的属性
    setattr(p,'id','162')
    print(p.id)
    
    # 从对象中删除属性
    delattr(p,'id')
    print(p.id)
    View Code

    使用场景

    反射被称为框架的基石,为什么
    因为框架的设计者,不可能提前知道你的对象到底是怎么设计的
    所以你提供给框架的对象 必须通过判断验证之后才能正常使用
    判断验证就是反射要做的事情,
    当然通过__dict__也是可以实现的, 其实这些方法也就是对__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)
    View Code

    动态导入

    """
    在框架设计中 我们不可能提前知道 框架的用户要提供类相关的信息
    """
    
    
    # 动态导入
    import importlib
    
    
    # 拿到模块
    p1 = importlib.import_module('libs.plugins')
    print(p1)
    
    # 从模块中取出类
    cls = getattr(p1,'WinCMD')
    print(cls)
    
    # 实例化产生对象
    obj = cls()
    obj.cd()
    类名称,属性名称,模块路径要保证大小写一致

    给myframework补充(已经实现的方法)

    import importlib
    from libs import plugins
    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')
    
    
    # 创建一个插件对象 调用框架来使用它
    wincmd = plugins.WinCMD()
    # 框架之外的部分就有自定义对象来完成
    # run(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)
    View Code

    元类

    元类:创建类的类

    对象是通过类实例化产生的,如果类也是对象的话,必然类对象也是有另一个类实例化产生的  
    
    默认情况下所有类的元类都是type 
    

      验证

    class Person(object):
        name = 'lanlan'
        pass
    
    p = Person()
    print(type(p))
    
    class Student:
        pass
    print(type(Student))
    View Code

    直接调用type类来产生类对象
    一个类的三个基本组成部分
    1.类的名字(字符类型) 2.类的父类们 (是一个元组或列表) 3.类的名称空间(字典类型)

    cls_obj = type('en',(),{})
    print(cls_obj)
    
    class Person():
        pass
    

     

    自定义元类

    """
    只要继承了type 那么这个类就变成了一个元类
    
    """
    
    # 定义元类
    class Mytype(type):
        def __init__(self,cls_name,bases,dict):
            super().__init__(cls_name,bases,dict)
            print(cls_name,bases,dict)
            if not cls_name.istitle():
                raise Exception('类名错误')
    
    
    # 为pig类 指定了元类为 MyType
    class Pig(metaclass=Mytype):
        pass
    
    
    class Duck(metaclass=Mytype):
        pass
    
    
    Mytype('Pig',(),{})
    View Code

    学习元类的目的

    高度的自定义一个类,例如控制类的名字必须以大驼峰的方式来书写  
    
    类也是对象,也有自己的类,
    
    我们的需求是创建类对象做一些限制 
    
    想到了初始化方法  我们只要找到类对象的类(元类),覆盖其中 init方法就能实现需求 
    
    当然我们不能修改源代码,所以应该继承type来编写自己的元类,同时覆盖init来完成需求
    

      

    元类中__call__方法

    当你调用类对象时会自动调用元类中的__call__方法 ,并将这个类本身作为第一个参数传入,以及后面的一堆参数
    
    覆盖元类中的call之后,这个类就无法产生对象,必须调用super().__call__来完成对象的创建  
    并返回其返回值 
    
    
    
    使用场景:
    
    当你想要控制对象的创建过程时,就覆盖call方法
    
    当你想要控制类的创建过程时,就覆盖init方法
    

     例子:

     

    class Mate(type):
        def __init__(self,name,bases,dict):
            super().__init__(name,bases,dict)
            print('init  ')
    
        def __call__(self, *args, **kwargs):
            print('call')
            print(self)
            print(args)
            print(kwargs)
            return super().__call__(*args,**kwargs)
    
    
    class Dog(metaclass=Mate):
        def __init__(self,name):
            self.name = name
    
        def __call__(self, *args, **kwargs):
            print('call')
    
    d = Dog('二狗子')
    print(d.name)
    
    
    注意:一旦覆盖了call必须调用父类的call方法来产生对象并返回这个对象 
    View Code

     

    案例:

    实现将对象的所有属性名称转为大写

     变成大写
    class Mate(type):
        def __call__(self, *args, **kwargs):
            new_args = []
            for i in args:
                new_args.append(i.upper())
    
    
            print(new_args)
            print(kwargs)
            return super().__call__(*new_args,**kwargs)
    
    class Person(metaclass=Mate):
        def __init__(self,name,pwd):
            self.name =name
            self.pwd = pwd
    
    p = Person('nana','qwer')
    print(p)
    print(p.name)
    print(p.pwd)
    View Code

    例子

    要求创建对象时必须以关键字参数形式来传参
    覆盖元类的__call__
    判断你有没有传非关键字参数 == 不能有位置参数
    有就炸
    class Mate(type):
    
        def __call__(self, *args, **kwargs):
            if args:
                raise Exception('不允许有位置参数')
    
            return super().__call__(*args,**kwargs)
    
        # 
        # def __call__(self, *args, **kwargs):
        #     # 创建一个空对象
        #     obj = object.__new__(self)
        #     # 让对象去初始化
        #     self.__init__(obj,*args,**kwargs)
        # 
        #     return obj
    
    
            
    
    
    
    
    
    class A(metaclass=Mate):
        def __init__(self,name):
            self.name = name
    
    # a = A('lanlan')
    
    a = A(name = 'lanlan')
    print(a.name)
    View Code

    __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)
    View Code
    __init__方法和__new__方法 都可以实现控制类的创建过程,__init__方法更简单
    

    单例模式

    设计模式:用于解决某种固定问题的套路
    例如:MVC  MTV等
    单例:指的是一个类产生一个对象
    为什么要使用单例:单例是为了节省 资源,当一个类的所有对象属性全部相同时,则没有必要创建多个对象
    

      

    class Person():
        def __init__(self,name,age):
            self.name = name
            self.age =age
        def say_hi(self):
            print('hello %s '%self.name)
    
        #获取对象的方法
        @staticmethod
        def get_instance():
             # 判断是否已经有了对象
            if hasattr(Person,'obj'):
                return getattr(Person,'obj')
            obj = Person('wewe',15)
            Person.obj = obj
            print('新的')
            return obj
    
    p = Person.get_instance()
    p.say_hi()
    
    p2 = Person.get_instance()
    p2.say_hi()
    
    p3 = Person.get_instance()
    p3.say_hi()
    普通模式
    class Single(type):
        def __call__(self, *args, **kwargs):
    #         判断是否有已经存在的对象
            if hasattr(self,'obj'):
                # 取值
                return getattr(self,'obj')
            # 没有就创建
            obj = super().__call__(*args,**kwargs)
            print('创建了')
            # 存入类中
            self.obj = obj
            return obj
    
    class Student(metaclass=Single):
        def __init__(self,name):
            self.name = name
    
    
    class Person(metaclass=Single):
        pass
    
    
    # 只会创建一个对象
    Person()
    Person()
    Person()
    单例元类

       

     

  • 相关阅读:
    【CentOS 7】关于php留言本网站的搭建
    linux系统的初化始配置(临时生效和永久生效)
    时间同步ntp服务的安装与配置(作为客户端的配置)
    CentOS 7设置服务的开机启动
    辅助模型——通信图
    一.面向对象概论
    辅助模型——包图
    构建图
    部署图
    辅助模型——状态机图
  • 原文地址:https://www.cnblogs.com/komorebi/p/11272984.html
Copyright © 2011-2022 走看看