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

    反射 refiect

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

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

    涉及的四个内置函数

    hasattr(对象名, 属性名) : 判断某个对象是否存在某个属性
    getattr(对象名, 属性名, 默认值) : 从对象中取出属性,第三个值是默认值,当属性不存在时返回默认值
    setattr(对象名, 属性名, 属性值) : 为对象增加新的属性和值
    delattr(对象名, 属性名) : 删除对象的属性
    class A:
        def __init__(self, name, age, gander):
            self.name = name
            self.age = age
            self.gander = gander
        def hai(self):
            print('hai')
    ​
    a = A('x', 20, 'man')
    ​
    # 判断某个对象是否存在某个属性
    if hasattr(a, 'name'):
        # 从对象中取出属性,第三个值是默认值,当属性不存在时返回默认值
        print(getattr(a, 'name', None))
    # 为对象增加新的属性和值
    setattr(a, 'id', 110)
    print(a.id)
    # 删除对象的属性
    delattr(a, 'id')
    ​
    if hasattr(a, 'id'):
        print(a.id)
    else:
        print('没有 id')
        
    print(getattr(a, 'hai')) 
    # >>>  <bound method A.hai of <__main__.A object at 0x00000209A0549C18>>
    func = getattr(a, 'hai')
    func()  # >>> hai

    应用场景:

    反射其实就是对一个对象属性的增删改查,也可以通过通过__dict__来操作,单语法繁琐

    另外一个主要的问题是,如果对象不是我自己写,是另一方提供的,我就必须判断这个对象是否满足要求,也就是是否有我需要的属性和方法

    框架设计方式:

    '''
    反射被称为框架的基石
    框架的设计值,不可能提前知道你的对象到底是怎么设计的,所以你提供给框架的对象必须通过判断验证之后才能正常使用,判断验证就是反射要做的事情
    '''
    '''插件部分'''
    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_PATH = 'libs.plugins.LinuxCMD'
            
    '''框架部分'''from frame import settings
    import importlib
    ​
    ​
    def run(obj):
        while True:
            cmd = input('请输入指令:')
            if cmd == 'exit':
                break
            # 因为无法确定框架使用者是否传入正确的对象,所以需要使用反射来检测
            # 判断对象是否具有处理这个指令的方法
            if hasattr(obj, cmd):
                # 取出该方法
                func = getattr(obj, cmd)
                # 执行该方法
                func()
            else:
                print('该指令不支持')
        print('退出')
    ​
    ​
    # 框架得根据配置文件拿到需要的类
    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 A:
        pass
    a = A()
    print(type(a))  # <class '__main__.A'>
    print(type(A))  # <class 'type'>
    # A类是通过type类实例化产生的

    学习元类的目的:

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

    类也是对象,也有自己的类

    class MyType(type):
        # 覆盖元类中的__init__方法
        def __init__(self, cls_name, bases, dict):
            # 调用元类中的__init__方法,使用其初始化功能
            super().__init__(cls_name, bases, dict)
            # 添加判断条件
            if not cls_name.istitle():
                raise Exception('首字母需要大写')
    ​
    class A(metaclass=MyType):
        pass
    print(type(A))

    元类中的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)
            # 注意: 一旦覆盖了__call__ 必须调用父类的call方法来产生对象并返回这个对象
            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)

    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__更简单

    单例设计模式

    用于解决某种问题的固定思路
    例如:MVC MTV等
    单例:指的是一个趔产生一个对象
    目的:单例是为了节省资源,当一个类的所有对象属性全部相同时,则没有必须要创建多个对象
    # 单例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()

     

  • 相关阅读:
    leetcode-5-Longest Palindromic Substring
    leetcode-3-Longest Substring Without Repeating Characters
    leetcode-2-Add Two Numbers
    正则匹配-题型总结
    Kmp简单运用
    最长回文子串
    树-leetcode做题笔记
    java对象与类
    【LeetCode & 剑指offer刷题】查找与排序题13:Merge Intervals
    【LeetCode & 剑指offer刷题】查找与排序题12:Top K Frequent Elements
  • 原文地址:https://www.cnblogs.com/waller/p/11272014.html
Copyright © 2011-2022 走看看