博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
深入理解 python 元类
阅读量:6503 次
发布时间:2019-06-24

本文共 7568 字,大约阅读时间需要 25 分钟。

一、什么的元类

# 思考:#     Python 中对象是由实例化类得来的,那么类又是怎么得到的呢?# 疑问:#     python 中一切皆对象,那么类是否也是对象?如果是,那么它又是那个类实例化而来的呢?
class student(object):    def __init__(self):        passjmz = student()print(type(jmz))  # 
实例化student 得来的print(type(student)) #
# 说明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))   # 
print(dog.__dict__)# {'__module__': '__main__', '__init__':
, 'eat':
, '__dict__':
, '__weakref__':
, '__doc__': None}# type 用法二: 创建动态类def __init__(self): passdef eat(self): print("cat eating...")cat = type('cat',(object,),{
"__init__":__init__,"eat":eat})cat1 = cat()cat1.eat() # cat eating...print(type(cat1)) #
print(cat.__dict__)# {'__init__':
, 'eat':
, '__module__': '__main__', '__dict__':
, '__weakref__':
, '__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 Jmzclass 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)# 使用mateclassclass 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 Jmzclass 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)# 使用mateclassclass 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,),{})    passStudent('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 Jmzclass 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 objclass Student(object,metaclass=Mymeta):    '''    这是学生类    '''    __school = "上海交大"    def __init__(self,name,age):        self.name = name        self.age = age    def get_school(self):        return self.__schooljmz = 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 Jmzclass 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.__instancemysql = 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 Jmzclass 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.__instanceclass 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 38853264egon = Teacher("egon",24)egon1 = Teacher("egon1",24)print(id(egon),id(egon1))# 31578992 31578992# 解释# 1、student 类和 teacher类都是调用元类产生的,不同的类调用元类就好产生不同的内容地址。不同的类也只会定义一次(正常的)# 2、 对象的产生是有调用了元类的__call__ 方法产生的,所以每次调用都返回相同的对象(单例)。
元类,单例实现

 

  

 

转载于:https://www.cnblogs.com/xiaobaiskill/p/9482586.html

你可能感兴趣的文章
CSS3秘笈第三版涵盖HTML5学习笔记9~12章
查看>>
bzoj1044木棍分割
查看>>
leetcode-136-Single Number
查看>>
微信小程序笔记<五> 页面管理及生命周期(route)——getCurrentPages()
查看>>
http服务器小项目
查看>>
JS案例:Jq中的fadeOut和fadeIn实现简单轮播(没完善,简单实现)
查看>>
一些数学上的名词及操作
查看>>
C# DataGridVie利用model特性动态加载列
查看>>
IPv6 地址分类
查看>>
<%@ include %>指令和<jsp:include>区别
查看>>
因为文件组 'PRIMARY' 已满 解决办法
查看>>
Flume 读取实时更新的日志文件
查看>>
HDU 2049
查看>>
《Spring1之第十次站立会议》
查看>>
Unity Shader 噪声消融特效 - 剑灵死亡特效
查看>>
Eclipse 自动生成 Ant的Build.xml 配置文件
查看>>
添加一条信息到列表,如果重复就替换,
查看>>
C#基础第五天
查看>>
MEF 编程指南(六):导出和元数据
查看>>
宝明34
查看>>