深入理解 python 元类

news/2024/7/2 22:27:16

一、什么的元类

# 思考:
#     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 元类实现单例模式

#! /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


http://lihuaxi.xjx100.cn/news/236406.html

相关文章

java map prefix_从键以特定表达式开头的Map中获取所有值的最快方法

小编典典如果您使用NavigableMap(例如TreeMap)&#xff0c;则可以利用基础树数据结构的好处&#xff0c;并执行以下操作(非常O(lg(N))复杂)&#xff1a;public SortedMap getByPrefix(NavigableMap myMap,String prefix ) {return myMap.subMap( prefix, prefix Character.MAX…

OSError: Could not find library geos_c or load any of its variants ['libgeos_c.so.1', 'libgeos_c.so

OSError: Could not find library geos_c or load any of its variants [libgeos_c.so.1, libgeos_c.so 解决&#xff1a; sudo vim /etc/ld.so.conf 添加&#xff1a;/opt/source/geos-3.5.0/build/lib sudo ldconfig

C语言程序试题

一个无向连通图G点上的哈密尔顿&#xff08;Hamiltion&#xff09;回路是指从图G上的某个顶点出发&#xff0c;经过图上所有其他顶点一次且仅一次&#xff0c;最后回到该顶点的路劲。一种求解无向图上哈密尔顿回路算法的基础实现如下&#xff1a; 假设图G存在一个从顶点V0出发的…

Guava Cache本地缓存在 Spring Boot应用中的实践

概述 在如今高并发的互联网应用中&#xff0c;缓存的地位举足轻重&#xff0c;对提升程序性能帮助不小。而 3.x开始的 Spring也引入了对 Cache的支持&#xff0c;那对于如今发展得如火如荼的 Spring Boot来说自然也是支持缓存特性的。当然 Spring Boot默认使用的是 SimpleCache…

php include include_once 区别,「PHP」include()、include_once()、require()、require_once()的用法及区别...

1、include&#xff1a;使用include引用外部文件时&#xff0c;只有代码执行到include代码段时&#xff0c;调用的外部文件才会被引用并读取&#xff0c;当引用的文件发生错误时&#xff0c;系统只会给出个警告错误&#xff0c;而整个php文件会继续执行。使用require语句来调用…

面试题收集最新

Java高级程序员面试题------https://www.cnblogs.com/mengdou/p/7233398.html Java高级工程师面试题总结及参考答案-----https://www.cnblogs.com/java1024/p/8594784.html Java高级程序员&#xff08;5年左右&#xff09;面试的题目集----https://blog.csdn.net/fangqun663775…

Clojure程序设计

《Clojure程序设计》基本信息作者&#xff1a; (美)Stuart Halloway Aaron Bedra [作译者介绍]出版社&#xff1a;人民邮电出版社ISBN&#xff1a;9787115308474上架时间&#xff1a;2013-3-1出版日期&#xff1a;2013 年3月开本&#xff1a;16开页码&#xff1a;230版次&#…

ubuntu系统php环境变量设置,Ubuntu系统环境变量详解

使用Ubuntu 进行开发绕不开的就是环境变量的配置&#xff0c;由于Linux系统严格的权限管理&#xff0c;造成Ubuntu系统有多个环境变量配置文件&#xff0c;如果不了解其调用顺序&#xff0c;很有可能遇到配置了环境变量&#xff0c;而没有其作用的问题。本文将介绍Ubuntu Linux…