sailan
一、元类解析
1、什么是元类
在Python中一切皆是对象,所有的对象都是实例化或者说调用类而得到的(调用类的过程称为类的实例化),
一切皆为对象,那么类本质也是一个对象,既然所有的对象都是调用类得到的,那么类必然也是调用了另一个类得到的,这个类称为元类;
# 元类=》OldboyTeacher类=》obj
class OldboyTeacher(object):
school = 'oldboy'
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print('%s says welcome to the oldboy to learn Python' % self.name)
obj = OldboyTeacher('egon', 18) # 调用OldboyTeacher类=》对象obj
# 调用元类=》OldboyTeacher类
print(type(obj))
print(type(OldboyTeacher))
<class '__main__.OldboyTeacher'>
<class 'type'>
结论: 默认的元类是type,默认情况下我们用class关键字定义的类都是由type产生的
2、class关键字创建类的底层流程
类的三大组成: 类名、基类、类的名称空间(执行代码得到)
1、先拿到一个类名
class_name = "OldboyTeacher"
2、然后拿到类的父类
class_bases = (object,)
3、再运行类体代码,将产生的名字放到名称空间中
class_dic = {}
class_body = """
school = 'oldboy'
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print('%s says welcome to the oldboy to learn Python' % self.name)
"""
exec(class_body,{},class_dic)
# print(class_dic)
4、调用元类(传入类的三大要素:类名、基类、类的名称空间)得到一个元类的对象,
然后将元类的对象赋值给变量名OldboyTeacher,oldboyTeacher就是我们用class自定义的那个类
OldboyTeacher = type(class_name,class_bases,class_dic)
二、自定义元类
一个类没有声明自己的元类,默认他的元类就是type,除了使用内置元类type,我们也可以通过继承type来自定义元类,然后使用metaclass关键字参数为一个类指定元类;
1、定义元类
class Mymeta(type): # 只有继承了type类的类才是自定义的元类
pass
class OldboyTeacher(object, metaclass=Mymeta):
school = 'oldboy'
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print('%s says welcome to the oldboy to learn Python' % self.name)
# 1、先拿到一个类名:"OldboyTeacher"
# 2、然后拿到类的父类:(object,)
# 3、再运行类体代码,将产生的名字放到名称空间中{...}
# 4、调用元类(传入类的三大要素:类名、基类、类的名称空间)得到一个元类的对象,然后将元类的对象赋值给变量名OldboyTeacher,oldboyTeacher就是我们用class自定义的那个类
OldboyTeacher = Mymeta("OldboyTeacher",(object,),{...})
2、自定义类来控制类的产生
import re
class Mymeta(type): # 只有继承了type类的类才是自定义的元类
def __init__(self, class_name, class_bases, class_dic):
# print(self) # 类<class '__main__.OldboyTeacher'>
# print(class_name)
# print(class_bases)
# print(class_dic)
if not re.match("[A-Z]", class_name):
raise BaseException("类名必须用驼峰体")
if len(class_bases) == 0:
raise BaseException("至少继承一个父类")
# print("文档注释:",class_dic.get('__doc__'))
doc=class_dic.get('__doc__')
if not (doc and len(doc.strip()) > 0):
raise BaseException("必须要有文件注释,并且注释内容不为空")
# OldboyTeacher = Mymeta("OldboyTeacher",(object,),{...})
class OldboyTeacher(object,metaclass=Mymeta):
"""
adsaf
"""
school = 'oldboy'
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print('%s says welcome to the oldboy to learn Python' % self.name)
3、自定义元类来控制类的调用
import re
class Mymeta(type): # 只有继承了type类的类才是自定义的元类
def __init__(self, class_name, class_bases, class_dic):
# print(self) # 类<class '__main__.OldboyTeacher'>
# print(class_name)
# print(class_bases)
# print(class_dic)
if not re.match("[A-Z]", class_name):
raise BaseException("类名必须用驼峰体")
if len(class_bases) == 0:
raise BaseException("至少继承一个父类")
# print("文档注释:",class_dic.get('__doc__'))
doc = class_dic.get('__doc__')
if not (doc and len(doc.strip()) > 0):
raise BaseException("必须要有文件注释,并且注释内容不为空")
# res = OldboyTeacher('egon',18)
def __call__(self, *args, **kwargs):
# 1、先创建一个老师的空对象
tea_obj = object.__new__(self)
# 2、调用老师类内的__init__函数,然后将老师的空对象连同括号内的参数的参数一同传给__init__
self.__init__(tea_obj, *args, **kwargs)
tea_obj.__dict__ = {"_%s__%s" %(self.__name__,k): v for k, v in tea_obj.__dict__.items()}
# 3、将初始化好的老师对象赋值给变量名res
return tea_obj
# OldboyTeacher = Mymeta("OldboyTeacher",(object,),{...})
class OldboyTeacher(object, metaclass=Mymeta):
"""
adsaf
"""
school = 'oldboy'
# tea_obj,'egon',18
def __init__(self, name, age):
self.name = name # tea_obj.name='egon'
self.age = age # tea_obj.age=18
def say(self):
print('%s says welcome to the oldboy to learn Python' % self.name)
res = OldboyTeacher('egon', 18)
print(res.__dict__)
# print(res.name)
# print(res.age)
# print(res.say)
# 调用OldboyTeacher类做的事情:
# 1、先创建一个老师的空对象
# 2、调用老师类内的__init__方法,然后将老师的空对象连同括号内的参数的参数一同传给__init__
# 3、将初始化好的老师对象赋值给变量名res
三、单例
单例: 即单个实例,指的是同一个类实例化多次的结果指向同一个对象,用于节省内存空间;
如果我们从配置文件中读取配置来进行实例化,在配置相同的情况下,就没必要重复产生对象浪费内存了
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
四、属性查找
结合python继承的实现原理+元类重新看属性的查找会是什么样,
class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
n=444
def __call__(self, *args, **kwargs): #self=<class '__main__.StanfordTeacher'>
obj=self.__new__(self)
self.__init__(obj,*args,**kwargs)
return obj
class Bar(object):
n=333
class Foo(Bar):
n=222
class StanfordTeacher(Foo,metaclass=Mymeta):
n=111
school='Stanford'
def __init__(self,name,age):
self.name=name
self.age=age
def say(self):
print('%s says welcome to the Stanford to learn Python' %self.name)
print(StanfordTeacher.n) #自下而上依次注释各个类中的n=xxx,然后重新运行程序,发现n的查找顺序为StanfordTeacher->Foo->Bar->object->Mymeta->type
属性查找分成两层,一层是对象层(基于c3算法的MRO)的查找,另外一个层则是类层(即元类层)的查找
查找顺序:
- 先对象层:StanfordTeacher->Foo->Bar->object
- 然后元类层:Mymeta->type