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

    1.反射 reflect

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

      涉及四个普通的内置函数,没有双下划线, hasattr    getattr     setattr      delattr

      hasattr   判断对象中是否有这个方法或变量

      getattr    获取对象中的方法或变量的内存地址

      setattr     为对象添加变量或方法

      delattr     删除对象中的变量(不能用于删除方法)

    def abc(self):
      print('%s正在交谈'%self.name)
    class
    Person: def __init__(self,name,age,male): self.name = name self.age = age self.male = male
      def talk(self):
         print('%s正在交谈'%self.name) p
    = Person('jack',18,'man') if hasattr(p,"name"): # 判断某个对象是否存在某个属性 print(getattr(p,'name',None)) # jack # 从对象中取出属性,第三个值位默认值 当属性不存在是返回默认值 if hasattr(p,"talk"): # 判断某个对象是否存在talk方法
      print(getattr(p,"talk","not find")) # 获得talk方法的内存地址 ,第三个参数用于当方法或属性不存在时,输出not find
    setattr(p,"a",abc) # 将abc函数添加到对象中,并命名为a
    p.talk(p)     # 调用a方法,因为这是额外添加的方法,需要手动传入对象
    setattr(p,
    'id','123') # 添加一个变量age,赋值为123 print(p.id) #123 delattr(p,'id') # 删除id变量 print(p.id) # 此时将报错

    使用场景:

      反射其实就是对属性的增删改查,但是如果直接使用内置的dict来操作,语法繁琐,不好理解, 另外一个最主要的问题是,如果对象不是我自己写的是另一方提供的,我就必须判断这个对象是否满足的要求,也就是是否我需要的属性和方法

    框架代码:

    反射被称为框架的基石, 因为框架的设计者,不可能提前知道你的对象到底是怎么设计的, 所以你提供给框架的对象 必须通过判断验证之后才能正常使用, 判断验证就是反射要做的事情, 通过__dict__也是可以实现的, 其实这些方法也就是对__dict__的操作进行了封装


    需求:要实现一个用于处理用户的终端指令的小框架
    框架就是已经实现了最基础的构架,就是所有项目都一样的部分

    import importlib
    import settings
    def run(plugin):
        while True:
            cmd = input('请输入指令').strip()
            if cmd == 'exit':
                break
            if hasattr(plugin,cmd):  # 取出对应方法
                func = getattr(plugin,cmd)
                func()
            else:
                print('该指令不支持')
        print('see you la la')
    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)   #调用框架

    settings 文件里的代码为
    CLASS_PATH = 'plugins.LinuxCMD'
    plugins 文件里的代码
    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 列出文件')
    class Person(object):
        name = '123'
    p = Person()
    print(type(p))    # <class '__main__.Person'>
    print(type(Person))   <class 'type'>

    调用type类可以产生类对象, 一个类的三个基本组成部分

      1.类的名字  2.类的父类们   3.类的名称空间

    cls_obj = type('dog',(),{})
      print(cls_obj)   #    <class '__main__.dog'>

    元类

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

    class Person:
        pass
    p = Person()  
    print(type(p))  # <class '__main__.Person'>
    print(type(p).__class__)  #<class 'type'>
    print(p.__class__)  # <class '__main__.Person'>
    print(type(Person)) #<class 'type'>
    print(type(Person).__class__) # <class 'type'>

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

    class MyType(type):    # 定义了一个元类
        def __init__(self,clss_name,bases,dict):
            super().__init__(clss_name,bases,dict)
            if not clss_name.istitle():
                raise Exception('类名有误')
    class Pig(metaclass = MyType):    # 为pig类指定了元类为MyType
        pass
    class MyType(type):
        def __call__(self, *args, **kwargs):
            new_args = []
            for a in args:
                new_args.append(a.upper())
            print(new_args)   # ['HAHA']
            print(kwargs)    # {'name': 'jack', 'gender': 'woman'}
            return super().__call__(*new_args,**kwargs)
    class Person(metaclass=MyType):
        def __init__(self,*args,name,gender):
            self.name = name
            self.gender = gender
    p = Person('haha',name="jack",gender="woman")
    print(p.name)   #jack
    print(p.gender)   #woman
    要求创建对象时,必须以关键字参数形式传参
    覆盖元类的__call__
    判断你有没有传非关键字参数 == 不能有位置参数, 有就炸
    class
    Mate(type): def __call__(self, *args, **kwargs): if args: raise Exception('不允许使用位置参数') return super().__call__(*args,**kwargs) class A(metaclass=Mate): def __init__(self,name): self.name = name a = A(name='jack') print(a.name) # jack

    元类中call方法

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

    使用场景:

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

    new方法

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

    class Meta(type):
        def __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):    # init run
        pass
    print(A)    #  <class '__main__.A'>
    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 Studenet(metaclass= Single):
        def __init__(self,name):
            self.name = name
    class Person(metaclass=Single):
        pass
    stu = Student('jack')
    stu = Student('jack')

    单例设计模式: 指的是一个类产生一个对象

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

     魔法方法

    class TestClass:
        def __new__(cls, *args, **kwargs):
            print("创建实例")
            return object.__new__(cls)
        def __setattr__(self,age,value):
            print("执行__setattr__")
            object.__setattr__(self,age,value)
        def __init__(self,name):
            self.name = name
            print("初始化实例")
        def __str__(self):
            return self.name
        def __getattr__(self, item):
            print("不存在的属性")
            return item
        def a(self):
            print("a")
        def __del__(self):
            print("销毁")
    
    t = TestClass('dayehui')
    print(t.age)
    t1 = TestClass(22)
    dayehui
  • 相关阅读:
    每天一点点之css
    【Vue中的坑】Vue中的修改变量没有效果?
    每天一点点之vue框架开发
    每天一点点之laravel框架开发
    每天一点点之vue框架开发
    推荐几款好用的办公软件
    每天一点点之vue框架开发
    Unity3D调用摄像头,画面为翻转的问题
    正确显示竖屏预览和拍照的照片
    卡尔曼滤波简介+ 算法实现代码
  • 原文地址:https://www.cnblogs.com/zrh-960906/p/11272786.html
Copyright © 2011-2022 走看看