• 元类,反射


    反射

    元类

    项目生命周期

    选课系统分析

      反射 reflect

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

    反射就是通过字符串操作属性

    反射涉及四个函数,这四个函数就是普通的内置函数,没有双下划线

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

    使用场景:

    反射其实就是对属性的增删改查,但是如果直接使用内置的__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)

    元类  metaclass

      一个类有三大组成部分,分别是

      1、类名class_name='OldboyTeacher'

      2、基类们class_bases=(object,)

      3、类的名称空间class_dic,类的名称空间是执行类体代码而得到的

      调用type时会依次传入以上三个参数

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

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

    验证:

    class Person:
        pass
    p = Person()
    
    print(type(p))
    print(type(Person))

    Person类是通过type类实例化产生的 

    元类可以高度的自定义一个类,例如控制类的名字必须用大驼峰的方式来书写

    类也是对象,也有自己的类,我们的需求是创建类对象做一些限制

    想到了初始化方法 我们只要找到类对象的类(元类),覆盖其中 init方法就能实现需求

    当然我们不能修改源代码,所以应该继承type来编写自己的元类,同时覆盖init来完成需求

    代码:

    # 定义了一个元类
    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

    元类中call方法

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

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

    使用场景:

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

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

    # 实现将对象的所有属性名称转为大写
    lass 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(name="jack",gender="woman")
    print(p.name)
    print(p.gender)

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

    补充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) # 上面的方法等同于这个方法super()是调取父类的方法,元类的父类就是type
            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更简单

    单例设计模式

    # 单例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
    
    # 只会创建一个对象
    t = Student('chen')
    t2 = Student('pi')
    print(t.name)
    print(t2.name)
    import threading
    class Singleton(object):
        _instance_lock = threading.Lock()
    
        def __init__(self):
            pass
    
    
        def __new__(cls, *args, **kwargs):
            if not hasattr(Singleton, "_instance"):
                with Singleton._instance_lock:
                    if not hasattr(Singleton, "_instance"):
                        Singleton._instance = object.__new__(cls)  
            return Singleton._instance
    
    obj1 = Singleton()
    obj2 = Singleton()
    print(obj1,obj2)
    
    def task(arg):
        obj = Singleton()
        print(obj)
    
    for i in range(10):
        t = threading.Thread(target=task,args=[i,])
        t.start()
  • 相关阅读:
    R学习笔记3 数据处理
    R学习笔记2 因子
    R学习笔记1 介绍R的使用
    正则表达式之邮箱、手机号码、电话号码,url地址
    vue之axios运用
    angularJS导出数据到Excel
    vue2全选反选
    css设置垂直居中
    js实现鼠标选中文本改变选中区域颜色以及给选中区域加上html标签
    安装了Vetur之后的配置
  • 原文地址:https://www.cnblogs.com/AbrahamChen/p/11272154.html
Copyright © 2020-2023  润新知