zoukankan      html  css  js  c++  java
  • 面向对象高级——反射和元类

    参考链接:https://www.cnblogs.com/yangyuanhu/p/11207450.html#autoid-0-0-0

    一、反射

    什么是反射,其实是反省,自省的意思

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

    涉及的四个函数,这四个函数就是普通的内置函数,没有双下划线,与print等等没有区别。

    class A:
        def __init__(self,name,age,gender):
            self.name = name
            self.age = age
            self.gender = gender
        def a(self):
            print('我是a')
    
    p = A('jack',18,'meal')
    
    print(hasattr(p,'name'))  #判断是否存在返回True或者False
    setattr(p,'id',50)  #设置属性id值为50
    
    getattr(p,'a')() #getattr获取到函数名,加括号就是调用
    print(getattr(p,'name'))  #getattr获取到属性值
    
    delattr(p,'id')#删除

    #结果
    True
    我是a
    jack

    使用场景:

    反射其实就是对属性的增删改查,但是如果直接使用内置的__dict__来操作,语法繁琐,不好理解。

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

    框架的设计方式:

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

    二、元类  metaclass

    元类是什么,用于创建类的类,万物皆对象,类也是对象

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

    class Person:
        pass
    p = Person()
    
    print(type(p))
    print(type(Person))
    Person类是通过type类实例化产生的
    #结果 <class '__main__.Person'> <class 'type'>

    学习元类的目的:

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

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

    想到了初始化方法,我们只要找到类对象的类(就是元类),覆盖其中init方法就能实现需求,当然我们不可能去修改源代码,所以应该继承type来编写自己的元类,同时覆盖init来完成需求

    重写init方法,一定要记得调用父类的init方法

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

    1、类名clss_name

    2、基类们base,用()或者[]

    3、类的名称空间dict {}

    自定义元类控制类person的创建

    """
    只要继承了type 那么这个类就变成了一个元类
    """
    # 定义了一个元类
    class MyType(type): #继承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 Person(metaclass=MyType):    #Person = MyType('Person',(object),...)
        def __init__(self,name,age)
        self.name = name
        slef.age = age
      def say(self):
        print('my name is %s'%self.name)

    创建流程:自定义元类可以控制类的产生过程,类的产生过程其实就是元类的调用过程,即Person =MyType('Person',(object),...),调用MyType先会产生一个空对象Person,然后将MyType括号内的参数一同传给MyType下的__init__方法,完成初始化。

    自定义元类控制类person的调用

    补充:__call__方法

    class A:
        def __call__(self, *args, **kwargs):
            print("call run")
            print(args)
            print(kwargs)
    
    a = A()
    a(1,a=100)    #想要a对象变成一个可调用对象,需要在类中定义一个方法__call__方法,该方法在调用对象的时候会自动触发,a的返回值就是__call__方法的返回值
    
    #结果
    call run
    (1,)
    {'a': 100}

    由上可知,调用一个对象的时候,就是触发所在类中的__call__方法,如果把Person也当做一个对象,那么在Person这个对象的类中也必然存在一个__calll__方法。

    class MyType(type):
        def __call__(self, *args, **kwargs):
            new_args = []
            for a in args:
                new_args.append(a.upper())
         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)
    #触发Person类中的__call__方法(使用MyType的),然后将Person传给self,调用Person的返回值就是调用__call__的返回值。

    默认地,调用对象p会做三件事:

    • 产生一个空对象obj   (__new__方法)
    • 调用__init__方法初始化对象obj
    • 返回初始化好的obj   return obj

    对应着,Person类中的__call__方法也应该做着三件事

    class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
        def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>
            #1、调用__new__产生一个空对象obj
            obj=self.__new__(self) # 此处的self是类OldoyTeacher,必须传参,代表创建一个OldboyTeacher的对象obj
    
            #2、调用__init__初始化空对象obj
            self.__init__(obj,*args,**kwargs)
    
            #3、返回初始化好的对象obj
            return obj
    
    class OldboyTeacher(object,metaclass=Mymeta):
        school='oldboy'
    
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def say(self):
            print('%s says welcome to the oldboy to learn Python' %self.name)
    
    t1=OldboyTeacher('egon',18)
    print(t1.__dict__) #{'name': 'egon', 'age': 18}
    当你调用类对象时会自动启动元类中的__call__方法 ,并将这个类本身作为第一个参数传入,以及后面的一堆参数
    覆盖元类中的call之后,这个类就无法产生对象,必须调用super().__call__来完成对象的创建  并返回其返回值 

    使用场景:

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

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

    class MyType(type):
        def __call__(self, *args, **kwargs):
            new_args = []
            for a in args:
                new_args.append(a.upper())
         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)
            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更简单

    元类总结:需要知道的魔法方法有:__call__,__new__,__init__

    流程:

    • 当调用对象的时候,首先会调用__call__方法,执行里面的代码
    • 然后调用__new__产生一个空对象
    • 调用__init__初始化空对象obj
    • 最后返回初始化好的对象obj

    需要注意的:可以重写__call__和__new__,如果不重写,就会调用元类type的(__call__如果重写了,一定要调用父类的__call__方法。__new__重写了,就必须要有返回值并且必须是对应的类对象)

    三、单例设计模式

    什么叫设计模式,就是解决某种固定问题的套路,例如: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()
  • 相关阅读:
    C#获取中英文混合字符串长度和截取函数
    页面和块高度在JavaScript中的属性总结
    跨浏览器兼容添加到收藏夹/书签的Javascript
    转:28个免费的在线文件格式转换工具
    2009年十大经典网络小说
    某企业网站建设步骤
    转:Web安全工具大汇聚
    枚举
    VS中不显示解决方案的解决方法
    SQL 分页存储过程
  • 原文地址:https://www.cnblogs.com/wangcuican/p/11271398.html
Copyright © 2011-2022 走看看