• 深入理解 python 元类


    一、什么的元类

    # 思考:
    #     Python 中对象是由实例化类得来的,那么类又是怎么得到的呢?
    # 疑问:
    #     python 中一切皆对象,那么类是否也是对象?如果是,那么它又是那个类实例化而来的呢?
    class student(object):
        def __init__(self):
            pass
    
    jmz = student()
    
    print(type(jmz))  # <class '__main__.student'>   实例化student 得来的
    print(type(student))    # <class 'type'>   
    # 说明student类 是实例化type得来的。但任然是类
    # 总结:  元类即类的类。  type 就是Python中的默认元类
    # 元类===》类====》对象 (对象是实例化类得来的,类是实例化元类得来的)

    二、类的产生过程

      2.1 类的三大关键组成部分

      1、类名

      2、类的基类们

      3、类的名称空间

      2.2 type讲解

       在Python中type 函数有两种完全不同 的用法,一个是用来返回对象的类,一个则是动态的创建类。(我知道,根据传入参数的不同,同一个函数拥有两种完全不同的用法是一件很傻的事情,但这在Python中是为了保持向后兼容性)

      2.3 type的两种用法

    # type用法一: 查看对象的类
    class dog(object):
        def __init__(self):
            pass
        def eat(self):
            print('dog eating...')
    
    dog1 = dog()
    dog1.eat()          # dog eating...
    print(type(dog1))   # <class '__main__.dog'>
    print(dog.__dict__)
    # {'__module__': '__main__', '__init__': <function dog.__init__ at 0x00000000021B9950>, 'eat': <function dog.eat at 0x00000000021B99D8>, '__dict__': <attribute '__dict__' of 'dog' objects>, '__weakref__': <attribute '__weakref__' of 'dog' objects>, '__doc__': None}
    
    # type 用法二: 创建动态类
    def __init__(self):
        pass
    def eat(self):
        print("cat eating...")
    cat = type('cat',(object,),{"__init__":__init__,"eat":eat})
    
    cat1 = cat()
    cat1.eat()          # cat eating...
    print(type(cat1))  # <class '__main__.cat'>
    print(cat.__dict__)
    # {'__init__': <function __init__ at 0x00000000004D1EA0>, 'eat': <function eat at 0x00000000022D9A60>, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'cat' objects>, '__weakref__': <attribute '__weakref__' of 'cat' objects>, '__doc__': None}
    # 分析:
    #     1、dog 类 与cat类功能基本一致,只是实现方式不同,一个使用了class 定义了类,一个使用了type产生了一个类
    #     2、dog类与cat类实例化方式一样,
    #     3、产生对象后的调用对象方法一样
    #     4、__dict__ 类的内容基本一致
    
    # 总结:
    #     class 关键字的底层实现就是做了与type方法一样的事

     

      2.4、 class关键字底层实现

    #1 拿到类名class_name="cat"
    #2 拿到基类们class_bases=(object,)
    #3 拿到名称空间 class_dic={...}
    #4 调用元类产生cat类,
    cat=type(class_name,class_bases,class_dic)

    三、元类的使用

      3.1 自定义元类

        Python 中默认元类就是type,所以要自定义元类就需要继承元类,继承元类的类任然是元类 

    #! /usr/bin/env python
    # -*- coding:utf-8 -*-
    # Author Jmz
    
    class Mymeta(type):   # 继承type元类
        def __init__(self,class_name,class_bases,class_dict):
            if not class_name.istitle():
                raise TypeError('类的名称首字母必须大写')
            if not class_dict.get('__doc__'):
                raise TypeError('类的定义必须要有参数')
            super(Mymeta, self).__init__(class_name, class_bases, class_dict)
    
    
    # 使用mateclass
    class People(object,metaclass=Mymeta):       # ===》 People = type("People",(object,),{名称空间})
        '''
        这是一个关于人的类
        '''
        def __init__(self,name,sex):
            self.name = name
            self.sex = sex
        def do(self,thing):
            print("%s 正在做%s"%self.name,thing)

      3.2 自定义元类产生类

       通过对于元类的创建,我们可以做到控制元类,约束类的创造。

    #! /usr/bin/env python
    # -*- coding:utf-8 -*-
    # Author Jmz
    
    class Mymeta(type):   # 继承type元类
        def __init__(self,class_name,class_bases,class_dict):  # class_name="People",class_bases=(object,),class_dict={名称空间}
            if not class_name.istitle():
                raise TypeError('类的名称首字母必须大写')
            if not class_dict.get('__doc__'):
                raise TypeError('类的定义必须要有参数')
            super(Mymeta, self).__init__(class_name, class_bases, class_dict)
    
    
    # 使用mateclass
    class People(object,metaclass=Mymeta):       # ===》 People = type("People",(object,),{名称空间})
        '''
        这是一个关于人的类
        '''
        def __init__(self,name,sex):
            self.name = name
            self.sex = sex
        def do(self,thing):
            print("%s 正在做%s"%self.name,thing)

      

      3.3 __call__ 方法

        对象是由类调用产生的,对象被当成方法调用,会触发类的__call__方法    

    class teacher(object):
        __addr ="上海校区"
        def __init__(self,name):
            self.name = name
        def select_class(self):
            pass
        def __call__(self, *args, **kwargs):   # 对象当成方法调用时会触发类的__call__ 方法执行
            print("对象调用触发")
            print(args)
            print(kwargs)
    
    
    egon = teacher('egon')
    egon(23,"",school = "oldboy")   
    # 对象调用触发
    # (23, '男')
    # {'school': 'oldboy'}

       

        类是由元类调用产生的,那么类被当成方法调用也应该触发元类的__call__方法

    class Mymeta(type):
        def __call__(self, *args, **kwargs):
            print("我是元类的__call__ 方法,类被调用时触发")
            print(args)
            print(kwargs)
    
    
    class Student(object,metaclass=Mymeta):   # Student = type("Student",(object,),{})
        pass
    
    
    Student('jmz',18,school="交大")
    # 我是元类的__call__ 方法,类被调用时触发
    # ('jmz', 18)
    # {'school': '交大'}

        从上面的代码我们可以看出,对象的产生其实就是,调用了类,进而触发了元类的__call__ 方法,

       但是调用类产生的是对象,说明元类的__call__ 方法是用来产生对象的。

        说明元类的__call__ 方法就做了下面的几件事

          1、创造了一个空对象

          2、调用了类的__init__ 方法

          3、将参入传入__init__方法中

          

      3.4 自定义元类产生对象

    #! /usr/bin/env python
    # -*- coding:utf-8 -*-
    # Author Jmz
    
    class Mymeta(type):  
        def __init__(self,class_name,class_bases,class_dict):
            if not class_name.istitle:
                raise TypeError("类的首字母必须大写")
            if not class_dict.get("__doc__"):
                raise TypeError("类的创建需写入注释说明")
    
            super(Mymeta,self).__init__(class_name,class_bases,class_dict)
    
        def __call__(self, *args, **kwargs):   # __call__  是由类调用的,所以self 就是类本身
            # 1、 产生一个空对象
            # 2、 将参数传入类的__init___方法中
    
            obj = object.__new__(self)   # 产生一个空对象,, __new__ 是object 的静态方法
            self.__init__(obj,*args,**kwargs)  # 调用类的__init__ 方法,self 就是类
    
            return obj
    
    
    
    class Student(object,metaclass=Mymeta):
        '''
        这是学生类
        '''
        __school = "上海交大"
        def __init__(self,name,age):
            self.name = name
            self.age = age
        def get_school(self):
            return self.__school
    
    
    jmz = Student("jmz",24)
    print(jmz.__dict__)
    print(jmz.get_school())
    # {'name': 'jmz', 'age': 24}
    # 上海交大
    # 上文解释:
       1、元类的__call__ 方法是由类被调用触发的(即类(),就会触发),
       2、类是实例化元类产生的,也可以说是元类产生的对象就是类,触发元类的__call__ 方法就是由类这个对象调用触发的,所以元类中的self 就是类。
       3、将参数传入类的__init__ 方法其实就是将参数传入元类中self的__init__方法。
       4、调用类的__init__ 方法与调用静态方法相同,该传的参数都要传

    四、为什么要使用元类

      从上面的如何使用元类中,我们可以看出,通过对于元类的定义,我们可以控制类的产生,和对象的产生。

    五、单例模式

      5.1 类实现单例模式

        单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。

    #! /usr/bin/env python
    # -*- coding:utf-8 -*-
    # Author Jmz
    
    class mysql(object):
        __instance = None
        def __init__(self):
            pass
    
        @classmethod
        def get_instance(cls,*args,**kwargs):
            if not cls.__instance:
                cls.__instance = cls(*args,**kwargs)
            return cls.__instance
    
    
    mysql = mysql.get_instance()
    print(mysql)
    # <__main__.mysql object at 0x00000000022D7BE0>
    mysql = mysql.get_instance()
    print(mysql)
    # <__main__.mysql object at 0x00000000022D7BE0>
    
    
    # 说明两个返回的内容地址是一样的,说明只实例化了一次。

      5.2 元类实现单例模式

    #! /usr/bin/env python
    # -*- coding:utf-8 -*-
    # Author Jmz
    
    class Mymeta(type):
        __instance = None
        def __init__(self,class_name,class_bases,class_dict):
            if not class_name.istitle:
                raise TypeError("类的首字母必须大写")
            if not class_dict.get("__doc__"):
                raise TypeError("类的创建需写入注释说明")
    
            super(Mymeta,self).__init__(class_name,class_bases,class_dict)
    
        def __call__(self, *args, **kwargs):   # __call__  是由类调用的,所以self 就是类本身
            # 1、 产生一个空对象
            # 2、 将参数传入类的__init___方法中
            if not self.__instance:
                obj = object.__new__(self)   # 产生一个空对象,, __new__ 是object 的静态方法
                self.__init__(obj,*args,**kwargs)  # 调用类的__init__ 方法,self 就是类
                self.__instance = obj
            return self.__instance
    
    class Student(object,metaclass=Mymeta):
        '''
        学生类
        '''
        def __init__(self,name,age,school):
            self.name = name
            self.age = age
            self.school = school
    
        def learn(self,thing):
            print("%s 正在学习%s"%(self.name,thing))
    
    class Teacher(object,metaclass=Mymeta):
        '''
        老师类
        '''
        def __init__(self,name,age):
            self.name = name
            self.age = age
        def teach(self,):
            print("%s 正在教书中。。。"%self.name)
    
    
    jmz = Student("jmz",23,"北大")
    jmz1 = Student("jmz1",23,"北大")
    
    print(id(jmz),id(jmz1))
    # 38853264 38853264
    
    
    egon = Teacher("egon",24)
    egon1 = Teacher("egon1",24)
    print(id(egon),id(egon1))
    # 31578992 31578992
    
    
    
    # 解释
    # 1、student 类和 teacher类都是调用元类产生的,不同的类调用元类就好产生不同的内容地址。不同的类也只会定义一次(正常的)
    # 2、 对象的产生是有调用了元类的__call__ 方法产生的,所以每次调用都返回相同的对象(单例)。
    元类,单例实现

      

  • 相关阅读:
    KMP算法
    模板特化
    css 绘制三角形和斜边
    709. 转换成小写字母
    numpy.ceil(), numpy.floor()
    warm_up
    Tensor基础概念
    模型微调
    @函数装饰器
    optimizer.step(), scheduler.step()
  • 原文地址:https://www.cnblogs.com/xiaobaiskill/p/9482586.html
Copyright © 2020-2023  润新知