深入理解 python 元类
一、什么的元类
# 思考: # Python 中对象是由实例化类得来的,那么类又是怎么得到的呢? # 疑问: # python 中一切皆对象,那么类是否也是对象?如果是,那么它又是那个类实例化而来的呢?
class student(object):def __init__(self):passjmz = 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):passdef 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 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)# 使用mateclass class People(object,metaclass=Mymeta): # ===》 People = type("People",(object,),{名称空间})'''这是一个关于人的类'''def __init__(self,name,sex):self.name = nameself.sex = sexdef 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)# 使用mateclass class People(object,metaclass=Mymeta): # ===》 People = type("People",(object,),{名称空间})'''这是一个关于人的类'''def __init__(self,name,sex):self.name = nameself.sex = sexdef do(self,thing):print("%s 正在做%s"%self.name,thing)
3.3 __call__ 方法
对象是由类调用产生的,对象被当成方法调用,会触发类的__call__方法
class teacher(object):__addr ="上海校区"def __init__(self,name):self.name = namedef select_class(self):passdef __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 = nameself.age = agedef 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 = Nonedef __init__(self):pass@classmethoddef 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 元类实现单例模式
![](/assets/blank.gif)
![](/assets/blank.gif)
#! /usr/bin/env python # -*- coding:utf-8 -*- # Author Jmzclass Mymeta(type):__instance = Nonedef __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 = objreturn self.__instanceclass Student(object,metaclass=Mymeta):'''学生类'''def __init__(self,name,age,school):self.name = nameself.age = ageself.school = schooldef learn(self,thing):print("%s 正在学习%s"%(self.name,thing))class Teacher(object,metaclass=Mymeta):'''老师类'''def __init__(self,name,age):self.name = nameself.age = agedef 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__ 方法产生的,所以每次调用都返回相同的对象(单例)。
元类,单例实现
转载于:https://www.cnblogs.com/xiaobaiskill/p/9482586.html
深入理解 python 元类相关推荐
- 深入理解Python元类(原创)
同样效果的代码: def __init__(cls,cls_name,cls_bases,cls_dict):type.__init__(cls,cls_name,cls_bases,cls_dict ...
- 深入理解python元类
类和对象 什么是元类 __metaclass属性 定制元类 为什么要使用元类 总结 类和对象 在理解什么是元类之前,有必要先理解下,什么是类. 什么是类?通俗的讲,类就是用来创建对象的代码片.在pyt ...
- 理解Python元类——e-satis
什么是元类?我们什么时候使用它? 翻译自StackOverflow:e-satis大神的回答 类也是对象 在理解元类之前,你需要掌握Python的类.Python从Smalltalk上借鉴了一个非常奇 ...
- python 元类工厂模式_Python进阶丨如何创建你的第一个Python元类?
摘要:通过本文,将深入讨论Python元类,其属性,如何以及何时在Python中使用元类. Python元类设置类的行为和规则.元类有助于修改类的实例,并且相当复杂,是Python编程的高级功能之一. ...
- 如何创建你的第一个Python元类?
Python元类设置类的行为和规则.元类有助于修改类的实例,并且相当复杂,是Python编程的高级功能之一.通过本文,将深入讨论Python元类,其属性,如何以及何时在Python中使用元类.本文介绍 ...
- Python进阶丨如何创建你的第一个Python元类?
摘要:通过本文,将深入讨论Python元类,其属性,如何以及何时在Python中使用元类. Python元类设置类的行为和规则.元类有助于修改类的实例,并且相当复杂,是Python编程的高级功能之一. ...
- python元类_Python元类
python元类 Welcome to today's tutorial on python metaclass. We all know that python is an object orien ...
- python元类_Python基础:元类
一.概述 Python虽然是多范式的编程语言,但它的数据模型却是 纯面向对象 的.与那些仅在语法层面声称纯OO的编程语言(如Java)相比,Python的这种纯粹性更加深入骨髓. 在Python的世界 ...
- Python元类详解
文章目录 Python元类详解 Python谜团 元类的本质 调用一个类时发生了什么 再探元类 自定义元类 彩蛋:跳过python解释器 Python元类详解 元类比99%的用户所担心的魔法要更深,如 ...
最新文章
- 高速串行总线系列(5)总线的各种基础问题
- java content()_理解content(一)
- SSH分客户端openssh-client和openssh-server
- ZH奶酪:Python按行读取文件
- Linux文件系统目录结构
- linux64位ioremap函数,linux操作系统中的ioremap函数详解
- 把运行在 Docker 容器内的 Microsoft SQL 服务器部署到 SAP Kyma 中
- 诡异的DateTime.TryParseExact方法
- MybatisCodeHelperNew-2.8.1-191-201插件使用
- python创建列表的语句_如何使用列表作为参数创建SELECT语句? - python
- mysql报错 Row size too large ( 8126)
- 史上最全的微信小程序代码大全
- Hadoop大数据开发整体思路
- JS 更合理的随机分组
- Notification基本通知的两种写法
- 2.2、云计算FusionCompute计算虚拟化
- 人工智能研究的新前线:生成式对抗网络
- Spring Cache key生成策略, 不要想当然认为是全类名+方法+参数
- 《机器学习算法竞赛实践》学习笔记(1)神经网络
- 恭喜猛龙获得NBA总冠军
热门文章
- MongoDB 标准连接字符串
- NetBeans配置Xdebug 远程调试PHP
- VS2005发布、生成网站时如何设置固定的dll文件名?
- 给DataGrid添加确定删除的功能
- Calendar如何只显示“一、二、三...日”,不显示“星期”
- 经典网络LeNet-5介绍及代码测试(Caffe, MNIST, C++)
- Linux Socket基础介绍
- 设计模式之策略模式(Strategy)摘录
- oracle web API,在Web API程序中使用Swagger做接口文档
- vue vuex 挂载_Vue $mount()手动挂载