元类
1 什么是元类:
源自一句话:在python中,一切皆对象,而对象都是由类实例化得到的
# class OldboyTeacher: # def __init__(self,name,age,sex): # self.name=name # self.age=age # self.sex=sex # # def score(self): # print('%s is scoring' %self.name) # tea1=OldboyTeacher('egon',18,'male') # # print(type(tea1)) # print(type(OldboyTeacher))
对象tea1是调用OldboyTeacher类得到的,如果说一切皆对象,那么OldboyTeacher也是一个对象,只要是对象
# 都是调用一个类实例化得到的,即OldboyTeacher=元类(...),内置的元类是type
# 关系:
1. 调用元类---->自定义的类
2. 调用自定义的类---->自定义的对象
class关键字创建自定义类的底层的工作原理,分为四步
1. 先拿到类名:'OldboyTeacher'
2. 再拿到类的基类们:(object,)
3. 然后拿到类的名称空间???(执行类体代码,将产生的名字放到类的名称空间也就是一个字典里,补充exec)
4. 调用元类实例化得到自定义的类
OldboyTeacher=type('OldboyTeacher',(object,),{...}) # class OldboyTeacher: #OldboyTeacher=type(...) # school = 'Oldboy' # def __init__(self,name,age,sex): # self.name=name # self.age=age # self.sex=sex # # def score(self): # print('%s is scoring' %self.name) # print(OldboyTeacher)
自定义类的三个关键组成部分:
1. 类名
2. 类的基类们
3. 类的名称空间
不依赖class关键字创建一个自定义类
# 1. 拿到类名 class_name='OldboyTeacher' #2. 拿到类的基类们:(object,) class_bases=(object,) #3. 拿到类的名称空间 class_dic={} class_body=""" school = 'Oldboy' def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex def score(self): print('%s is scoring' %self.name) """ exec(class_body,{},class_dic) print(class_dic) #4. 调用type得到自定义的类 OldboyTeacher=type(class_name,class_bases,class_dic) print(OldboyTeacher) # print(OldboyTeacher.school) # print(OldboyTeacher.score) tea1=OldboyTeacher('egon',18,'male') print(tea1.__dict__)
#总结:对象之所以可以调用,是因为对象的类中有一个函数__call__ #推导:如果一切皆对象,那么OldboyTeacher也是一个对象,该对象之所可以调用,肯定是这个对象的类中也定义了一个函数__call__
class Mymeta(type): #但凡继承了type的类才能称之为自定义的元类,否则就是只是一个普通的类 def __call__(self, *args, **kwargs): #self=OldboyTeacher这个类,args=('egon',18,'male'),kwargs={} # 1. 先产生一个空对象 tea_obj=self.__new__(self) #tea_obj是OldboyTeacher这个类的对象 # 2. 执行__init__方法,完成对象的初始属性操作 self.__init__(tea_obj,*args,**kwargs) # 3. 返回初始化好的那个对象 return tea_obj class OldboyTeacher(object,metaclass=Mymeta): #OldboyTeacher=Mymeta('OldboyTeacher',(object,),{...}) school = 'Oldboy' # tea_obj,'egon',18,'male' def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex def score(self): print('%s is scoring' %self.name) tea1=OldboyTeacher('egon',18,'male') # 会触发OldboyTeacher的类(即元类)中的__call__
实例化会
1. 先产生一个空对象
2. 执行__init__方法,完成对象的初始属性操作
3. 返回初始化好的那个对象
基于元类实现单例模式
#步骤五:基于元类实现单例模式 # 单例:即单个实例,指的是同一个类实例化多次的结果指向同一个对象,用于节省内存空间 # 如果我们从配置文件中读取配置来进行实例化,在配置相同的情况下,就没必要重复产生对象浪费内存了 #settings.py文件内容如下 HOST='1.1.1.1' PORT=3306 #方式一:定义一个类方法实现单例模式 import settings class Mysql: __instance=None def __init__(self,host,port): self.host=host self.port=port @classmethod def singleton(cls): if not cls.__instance: cls.__instance=cls(settings.HOST,settings.PORT) return cls.__instance obj1=Mysql('1.1.1.2',3306) obj2=Mysql('1.1.1.3',3307) print(obj1 is obj2) #False obj3=Mysql.singleton() obj4=Mysql.singleton() print(obj3 is obj4) #True #方式二:定制元类实现单例模式 import settings class Mymeta(type): def __init__(self,name,bases,dic): #定义类Mysql时就触发 # 事先先从配置文件中取配置来造一个Mysql的实例出来 self.__instance = object.__new__(self) # 产生对象 self.__init__(self.__instance, settings.HOST, settings.PORT) # 初始化对象 # 上述两步可以合成下面一步 # self.__instance=super().__call__(*args,**kwargs) super().__init__(name,bases,dic) def __call__(self, *args, **kwargs): #Mysql(...)时触发 if args or kwargs: # args或kwargs内有值 obj=object.__new__(self) self.__init__(obj,*args,**kwargs) return obj return self.__instance class Mysql(metaclass=Mymeta): def __init__(self,host,port): self.host=host self.port=port obj1=Mysql() # 没有传值则默认从配置文件中读配置来实例化,所有的实例应该指向一个内存地址 obj2=Mysql() obj3=Mysql() print(obj1 is obj2 is obj3) obj4=Mysql('1.1.1.4',3307) #方式三:定义一个装饰器实现单例模式 import settings def singleton(cls): #cls=Mysql _instance=cls(settings.HOST,settings.PORT) def wrapper(*args,**kwargs): if args or kwargs: obj=cls(*args,**kwargs) return obj return _instance return wrapper @singleton # Mysql=singleton(Mysql) class Mysql: def __init__(self,host,port): self.host=host self.port=port obj1=Mysql() obj2=Mysql() obj3=Mysql() print(obj1 is obj2 is obj3) #True obj4=Mysql('1.1.1.3',3307) obj5=Mysql('1.1.1.4',3308) print(obj3 is obj4) #False