类的补充:元类、元类创建过程、单例、元类实现单例
本文目录:
一、元类
元类是什么?
本质上也是一个类,元类是用于实例化其他类,对象怎么来的是类实例化出来的,那类这个对象怎么来的呢?就是由元类实例化出来的。因为在在python中一切皆对象。
# class Person: # pass # 类也是对象 在使用上与函数对象没啥区别 # obj = Person # li = [Person] # # def func(cls): # print(cls) # func(Person) class Dog: def __init__(self): print('狗的初始化了') color = 'red' def talk(self): print('狗叫了') d1 = Dog() print(Dog.__dict__) # 查看对象是哪个类实例化出来的 print(d1.__class__) print(Dog.__class__) print(type(d1)) print(type(Dog))
# 一个类必须具备的内容 """ 1.类名 2.基类 3.名称空间 """ # 通过查看__class__发现Dog对象是有type类实例化出来的 # 既然如此,是不是可以手动调用type来实例化 class_name = "Pig" bases = (object) pic_dict = {} class_body = """ def __init__(self): print("猪的初始化") color = "red" def talk(self): print("猪在叫") """ # 执行一堆字符串代码 将生产的内容放到pic_dict中 exec(class_body,{},pic_dict) print(pic_dict) # 调用type产生一个类 c = type(class_name,bases,pic_dict) print(c) print(Dog) """ 默认情况下 所有的类都是通过type这个元类实例化的 我们完全可以自己实例化 元类的作用 用于创建类的类 成为元类 """
二、通过元类来控制类的创建过程
class MyMetaclass(type): # 什么时候执行? MyMetaclass 定义一个类是 系统会自动去调用元类 def __init__(cls,class_name,bases,namespace): # 既然创建类时 会自动执行 该方法 那完全可以编写一些逻辑在init中 来控制类的创建 # 首字母必须大写否则不让创建 if not class_name.istitle(): raise TypeError("类名首字母必须大写!") if object not in bases: raise TypeError("必须显式的继承object") print(cls) print(class_name) print(bases) print(namespace) # 当求解释器执行到这行diamante时 自动调用了MyMetaclass # class Foo(metaclass=MyMetaclass): # 等同于 Foo = MyMetaclass() # attr = 123 # pass class Foo1(object,metaclass=MyMetaclass): # 等同于 Foo = MyMetaclass() attr = 123 pass
三、控制类的调用(实例化)
class Bar: def __call__(self,*args,**kwargs): print("run call") # 调用类时 还是调用对象时执行 b1 = Bar() b1() print(b1) # 在调用对象时,自动触发了__call__的执行 # 推到b1是Bar的实例,调用b1会触发bar中的__call__ # Bar是Type的实例,调用Bar应当触发type中的__call__ class MyMetaClass(type): def __call__(self, *args, **kwargs): print("MyMetaClass __call__ run!") print(self) # 需求判断实例化时的参数必须是字符串类型 if type(args[0])!= str: raise TypeError('名字必须是字符串类型!') # 这是自定义元类时,必须要有的模板,以保证可以正常实例化产生对象 obj = object.__new__(self) obj.__init__(*args,**kwargs) return obj class Foo(metaclass=MyMetaClass): def __init__(self,name): self.name = name pass # 调用类本质上就是调用__call__ 其返回值表示实例化得到的对象 res = Foo('bbb') print(res) # 调用一个类,创建出一个空对象,调用__init__来完成对象的初始化,返回该对象 # 控制类的调用,也就是实例化过程,核心函数元类中的__call__ # 需要注意的是,在__call__中应当先完成基础的逻辑1.创建空对象 2.执行__init__ 3.返回新对象 # 在此基础上添加新的业务逻辑
四、单例模式
""" 单例 是一种设计模式 是设计模式中比较简单的 指的是 一个类有且仅有一个实例 就叫单例 实现单例 就通过判断是否已经创建过对象 为什么要使用单例这种模式 之前在创建对象时 每个对象中的数据不相同 对象实际上数据和处理数据的方法的结合体 当对象中的数据是 相同的 共享的 时候 使用单例 u1 = user("张三",29,"man") u2 = user("张三",29,"man") u3 = user("张三",29,"man") 不同的对象 有完全相同的数据 没有必要每个人保存一份 u1 = user("张三",29,"man") u2 = u1 u3 = u1 如此 可以减少资源开销 大家共享一个数据 只有一个对象 """ class User: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex instance = None # 通过指定的方法来获取实例 而不是调用类来创建新对象 @classmethod def get_instance(cls,name,age,sex): if not cls.instance: cls.instance = cls(name,age,sex) return cls.instance @staticmethod def get_instance2(name,age,sex): if not User.instance: User.instance = User(name,age,sex) return User.instance # u1 = User("张三",20,"man") # u2 = User("张三",20,"man") u1 = User.get_instance2("张三",20,"man") print(u1) u2 = User.get_instance2("张三",20,"man") print(u2) # 通过classMethod 可以完成单例 但是还是可以通过直接调用;类产生新对象 此时就需要用到元类 u3 = User("张三",20,"man")
五、元类实现单例
class MyMetaClass(type): instance = None def __call__(cls, *args, **kwargs): if not MyMetaClass.instance: # 创建空对象 MyMetaClass.instance = object.__new__(cls) print("创建新的播放器对象!") #初始化对象 MyMetaClass.instance.__init__(*args,**kwargs) # 返回对象 return MyMetaClass.instance # 只能有一个播放器实例 class CDPlayer(metaclass=MyMetaClass): def play(self,music): print("切换音乐",music) def __init__(self,music_name): self.music_name = music_name p1 = CDPlayer("你发如雪!") p1.play("菊花台") p1.play("菊花台2") p1.play("菊花台3") # 不会创建新对象 p1 = CDPlayer("你发如雪!") p1 = CDPlayer("你发如雪!") p1 = CDPlayer("你发如雪!") p1 = CDPlayer("你发如雪!") # 元类实现单例 就是拦截了元类中的__call__的正常执行 使得创建对象都必须经过自己判断逻辑 # 类中的__init__也被拦截了
资料参考:
https://www.jianshu.com/p/c1ca0b9c777d(python元类)