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

    一:反射

    【1】基础概念:

    (1)定义:反射指的是一个对象 应该具备 修改 检测 增加属性的能力

        本质:属性的增删改查

    (2)使用场景:

        (1)当我框架搭建出来 需要向框架内部添加细节的时候 但是不知道该细节中内对象有什么属性 类支持什么功能 

        (2)此时可以通过反射询问对象含有什么属性 类支持什么功能

    (3)涉及的四个函数

      (1)hasattr

        作用:查看某对象是否含有某些属性

    例如:

    class Person:
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
    p = Person('SR',18)
    
    print(hasattr(p,'name'))  # True

      

      (2)getattr

          作用:通过该函数可以获取对象的属性

    例如:

    class Person:
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
    p = Person('SR',18)
    
    if hasattr(p,'name'):
        print(getattr(p,'name',None))  # SR
        print(getattr(p, 'names', None)) # None

    PS:

    (1)如果对象属性不存在 会报错

    (2)也可以设置默认值参数 属性不存在返回None

      (3)setattr

        作用:设置对象的属性

    例如:

    class Person:
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
    p = Person('SR',18)
    
    setattr(p,'id_cart','123')
    print(p.id_cart)  # 123

      (4)delattr

        作用:删除对象的属性

    例如:

    class Person:
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
    p = Person('SR',18)
    
    delattr(p,'id_cart')
    print(p.id_cart)  # 报错 上述属性已经被删除

    PS:上述四个方法都是属于普通函数 没有双下划线 非类内值的方法

    反射的案例:

    例如:

    def run(self):
        while True:
            cmd = input('请输入你要执行的命令>>:')
    
            # 如果输入exit退出
            if cmd == 'exit':
                break
    
            # 判断类是否有该方法
            if hasattr(self,cmd):
    
                # 如果有该方法 获取
                func= getattr(self,cmd)
                # 通过获取的方法调用
                func()
    
            else:
                print('命令不支持!')
    
    
    
    win = Wincmd()
    linux = Linuxcmd()
    
    # 将对象以参数形式传入 调用类中的方法以及属性
    run(win)
    run(linux)
    反射案例

    (2)动态导入:

      作用:

       (1)当我框架设计好的时候 开始使用对象以及类

         (2)但是作为框架的设计者 并不能知道对方的类叫什么名字 对象叫什么名字

       (3)因此我们为框架的使用者创建一个配置文件 框架使用者将类的信息传入配置文件中

       (4)框架设计者在该配置文件中调用所需的类

    例如:

    def run(self):
        while True:
            cmd = input('请输入你要执行的命令>>:').strip()
    
            # 如果输入exit退出
            if cmd == 'q':
                break
    
            # 判断类是否有该方法
            elif hasattr(self,cmd):
    
                # 如果有该方法 获取
                func= getattr(self,cmd)
                # 通过获取的方法调用
                func()
    
            else:
                print('命令不支持!')
    
    import importlib
    import settings
    
    # 在框架设计者 通过配置文件获取 使用者的类路径 以及类名称
    PATH = settings.CLASS_PATH
    # print(PATH)  # lib.plugins.Wincmd
    
    # 从模块中分别取出路径 以及类名称
    
    module_path,class_name = PATH.rsplit(".",1)
    # print(module_path) # lib.plugins
    # print(class_name) # Wincmd
    
    # 通过导入路径名称 拿到模块
    path = importlib.import_module(module_path)
    
    # 通过模块调取内部的类
    cls = path.Wincmd
    
    # 进行类的实例化 获取对象
    obj = cls()
    
    
    run(obj)  # 正在执行cd命令!
    动态导入

    PS:

      (1)框架使用者可以自己定义自己的名称 只需将类名称以及路径设置配置文件即可

      (2)框架设计者无需管框架使用者类中的名称以及属性 其只要从配置文件调用即可

    二:元类

    【1】基础概念

    (1)定义:是用来创建类的类

        解释:既然万物皆对象 类也属于对象 类对象就是通过元类产生的

    例如:

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

    PS:类都是有type类产生的

    (2)自定义类:

      (1)基本组成:

        (1)类名

        (2)基类

        (3)名称空间

    例如:

    cls = type('Name',(),{})
    print(cls)  # <class '__main__.Name'>

     (3)作用:高度的自定义自己需要的类

    例如:

    # 自定义一个元类
    class My_class(type):
        # 调用元类初始化方法
        def __init__(self,class_name,bases,dict):
            super().__init__(class_name,bases,dict)
    
            # 判断类名是否已大写开头
            if not class_name.istitle():
                raise Exception ('类名不会写吗!')
    
    # 指定其所对应的元类
    class Person(metaclass=My_class):
        pass
    
    class person(metaclass=My_class): # Exception: 类名不会写吗!
        pass

    PS:通过上述案例 自己定义一个类 要求类名必须为大写字母开头

    基本思路:

      (1)首先类也是对象 我们想在创建类的时候做限制

      (2)既然想在创建的类的时候做限制 可以使用初始化方法 本身我们不能随便修改源代码 但是我们可以调用元类中的初始化方法

      (3)子类通过覆盖元类中的初始化方法实现需求

    (4)call方法的应用

        作用:在对象被调用对象的时候 会执行该方法

    基础案例:

    class My_class(type):
        def __init__(self,name,bases,dict):
            super().__init__(name,bases,dict)
    
    
        def __call__(self, *args,**kwargs):
            print(self)  # <class '__main__.Dog'>
            print(*args) # 大黄
            print(**kwargs) #{}
            # 返回对象
            return super().__call__(*args,**kwargs)
    
    class Dog(metaclass=My_class):
    
        def __init__(self,name):
            self.name = name
    
        def __call__(self, *args, **kwargs):
            print('call run')
    
    
    # 执行该方法的时候 会调用元类中的call方法
    d = Dog('大黄')
    # 在元类中没有返回对象的时候
    print(d)  # None 而不是一个对象
    
    # 元类中返回对象
    print(d) # <__main__.Dog object at 0x00000000027F8B70>
    call基础案例

    PS:

    (1)当你调用类对象的时候 会自动调用元类中的call方法 并且将这个类本身作为第一个参数传给元类 以及后面的一堆参数

    (2)但是在自定义元类中因为调用了type这个元类 会覆盖原有元类call方法 这样原来的call方法就不能实例化对象 必须调用super().__call__来完成对象的创建

    call进阶版:

    # 将对象姓名变为大写
    
    class My_class(type):
        # 调用call方法 可以操作对象
        def __call__(self, *args, **kwargs):
            new_args = []
            # 循环打印 创建新的
            for i in args:
                new_args.append(i.upper())
                # 将新的对象返回
            return super().__call__(*new_args,**kwargs)
    
    
    
    
    
    class Person(metaclass=My_class):
        def __init__(self,name):
            self.name = name
    
    p = Person('jack')
    print(p.name) # JACK
    call案例

    PS:

    (1)如果操作类调用init方法 因为类的创建是初始化过程 init用来初始化

    (2)操作对象可以调用call方法 对象的产生就是调用call方法 可以通过call方法 影响对象

    (5)__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)

    PS:

    (1)首先会执行元类中的new方法 拿到一个空对象 然后自动调用init来对这个类进行初始化操作

    (2)如果你覆盖了该方法 必须保证new方法有返回值 且为该类对应的 对象

    (6)元类的单例设计模式:

      (1)设计模式:指的是解决某种问题的固定模式
      (2)单例:一个类只产生一个对象

      作用:为了节省空间 一个类全部对象属性都相同时候 可以使用单例

    例如:

    1. # 单例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
  • 相关阅读:
    软件构造—— 实验二 lex词法分析
    软件构造-实验1 根据状态转换图手工构造词法扫描器
    PHP——实验四 PHP操作数据库
    判断是不是素数
    hexo和github pages的关系
    Python的map,reduce,filter函数
    CentOS源码更新Linux最新内核
    CentOS打Meltdown等漏洞的补丁包
    let申明与const申明
    正则表达式
  • 原文地址:https://www.cnblogs.com/SR-Program/p/11271283.html
Copyright © 2011-2022 走看看