元类知识
一、python中一切皆对象
1.1 所有的对象都是实例化(或者叫通过调用类)而得到的
class People:
def __init__(self,name):
self.name = name
def run(self):
pass
peo1 = People('cc')
调用类的过程称之为实例化,如上面的peo1对象就是通过调用类People得到的
1.2 类本质上也是一种对象
那么类这种对象是怎么实例化得到的呢?或者说类是通过调用什么类产生的呢?
答案就是:元类
即上面的类People是通过People=元类(…)产生的
print(type(People)) # <class 'type'>
证明:
People也是调用了type这个元类而产生的People,即默认的元类就是type
默认情况下,使用class关键字定义的类都是由type实例化产生的
#
二、class关键字创建类的流程
2.1 类的组成部分
① 类名:class_name = 'People'
② 基类:base_class = (object, )
③ 类的名称空间:class_dic,由执行类体代码而得到的
调用type时会依次传入以上几种参数
2.2 class关键字在创建类的流程细分为四部分
# 1、先拿到一个类名
class_name = 'People'
# 2、拿到类的基类的名字
class_bases = (object,)
# 3、再运行类体代码,将产生的名字放进类的名称空间中
class_dic = {}
class_body = """
def __init__(self,name):
self.name = name
def run(self):
pass
"""
exec(class_name, class_bases, class_dic)
# 4、调用元类,传入类的三大元素:(类名,基类,类的名称空间)得到一个元类的对象,
# 然后将元类的对象赋值给变量名People,
# 类People就是我们用class自定义的那个类
People = type(class_name, class_bases, class_dic)
三、自定义元类
一个类如果没有声明自己的元类,那么默认他的元类就是type,
除了使用内置元类type,我们也可以通过继承type来自定义元类,然后使用metaclass参数来指定这个类的指定元类
只有继承了type的类才是自定义的元类,否则就是一个自定义的普通的类
示例:
class Mymeta(type):
pass
class People(object, metaclass=Mymeta):
def __self__(self,name,age):
self.name = name
self.age = age
def talk(self):
print('hello!')
# 1、先拿到一个类名:"People"
# 2、然后拿到类的父类:(object,)
# 3、再运行类体代码,将产生的名字放到名称空间中{...}
# 4、调用元类(传入类的三大要素:类名、基类、类的名称空间)得到一个元类的对象,
然后将元类的对象赋值给变量名People
People = Mymeta("People",(object,),{...})
四、使用元类来控制 类的产生
import re
class Mymeta(type): # 只有继承了type类的类才是自定义的元类
def __init__(self, class_name, class_bases, class_dic):
# print(self) # 类<class '__main__.People'>
# print(class_name) # People
# print(class_bases) # <class 'object'>
# 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("必须要有文件注释,并且注释内容不为空")
class Peolpe(object, metaclass=Mymeta):
"""
People的文档注释
"""
def __init__(self, name, age):
self.name = name
self.age = age
def talk(self):
print('hello!')
六、使用自定义元类来控制 类的调用
调用类People做的事情:
① 先创建一个类的空对象
② 调用类的__init__方法,然后将People类的空对象连同括号内的参数一起传给__init__
③ 将初始化好的对象赋值给变量名p1
示例:
import re
class Mymeta(type): # 只有继承了type类的类才是自定义的元类
def __init__(self, class_name, class_bases, class_dic):
if not re.match("[A-Z]", class_name):
raise BaseException("类名必须用驼峰体")
if len(class_bases) == 0:
raise BaseException("至少继承一个父类")
doc = class_dic.get('__doc__')
if not (doc and len(doc.strip()) > 0):
raise BaseException("必须要有文件注释,并且注释内容不为空")
def __call__(self, *args, **kwargs):
# 1、先创建一个老师的空对象
tea_obj = object.__new__(self)
# 2、调用老师类内的__init__函数,然后将老师的空对象连同括号内的参数的参数一同传给__init__
self.__init__(obj, *args, **kwargs)
# 将People类对象的属性隐藏起来(变成私有的)
obj.__dict__ = {"_%s__%s" %(self.__name__,k): v for k, v in tea_obj.__dict__.items()}
# 3、将初始化好的老师对象赋值给变量名res
return obj
class People(object, metaclass=Mymeta):
"""
People的文档注释
"""
def __init__(self, name, age):
self.name = name
self.age = age
def talk(self):
print('hello!')
p1 = People('cc', 18)
print(p1.__dict__) # {'_People__name': 'cc', '_People__age': 18}
七、单例模式
-
单例:
即单个实例,指的是同一个类的实例化多次的结果指向同一个对象,用于节省内存空间 如果我们从配置文件中读取配置来进行实例化,在配置相同的情况下,就没必要重复产生对象浪费内存了
用来实现,调用类创建对象时,当括号内有参数时就使用括号内传入的,
当没有传入参数时,就默认使用类产生同一个对象
示例:
settings文件内容如下:
IP = '192.168.11.188'
PORT = 8080
7.1 实现方式一:使用classmethod定义一个类方法
import settings
class MySQL:
__instance = None
def __init__(self, ip, port):
self.ip = ip
self.port = port
@classmethod
def singleton(cls):
if cls.__instance:
return cls.__instance
cls.__instance = cls(settings.IP, settings.PORT)
return cls.__instance
obj1 = MySQL.singleton()
print(obj1) # None
obj2 = MySQL('1.1.1.1', 8080)
obj3 = MySQL('1.1.1.2', 8080)
print(obj2) # <__main__.MySQL object at 0x0000023FFBCA5A30>
print(obj3) # <__main__.MySQL object at 0x0000023FFBCE11F0>
7.2 定制元类实现单例模式
import settings
class Mymeta(type):
__instance = None
def __init__(self, class_name, class_bases, class_dic):
self.__instance = object.__new__(self)
self.__init__(self.__instance, settings.IP, settings.PORT)
super().__init__(class_name, class_bases, class_dic)
def __call__(self, *args, **kwargs):
if args or kwargs:
obj = object.__new__(self)
self.__init__(obj, *args, **kwargs)
return obj
else:
return self.__instance
class Mysql(metaclass=Mymeta):
def __init__(self, ip, port):
self.ip = ip
self.port = port
obj1 = Mysql()
obj2 = Mysql()
print(obj1) # <__main__.Mysql object at 0x0000022A0F754AF0>
print(obj1 is obj2) # True
obj3 = Mysql('1.1.1.1', 8080)
print(obj3) # <__main__.Mysql object at 0x0000022A0F725A30>
7.3 定义一个装饰器实现单例模式
import settings
def outer(func):
_instance = func(settings.IP, settings.PORT)
def wrapper(*args, **kwargs):
if args or kwargs:
return func(*args, **kwargs)
return _instance
return wrapper
@outer
class MySQL:
def __init__(self, ip, port):
self.ip = ip
self.port = port
obj1 = MySQL()
obj2 = MySQL()
print(obj1 is obj2) # True
obj3 = MySQL('127.0.0.1', 8080)
obj4 = MySQL('127.0.0.2', 8080)
print(obj3) # <__main__.MySQL object at 0x000001F3BFE510D0>
print(obj4) # <__main__.MySQL object at 0x000001F3BFE51430>
八、属性查找
属性查找分为两层:
一层是对象层(基于c3算法的MRO)的查找
另一层是类层(元类层)的查找
查找顺序:
先对象层:OldboyTeacher->Foo->Bar->object
然后元类层:Mymeta->type
解释:
当OldboyTeacher 产生的对象jason调用某个属性时,
查找顺序为:jason->OldboyTeacher->Foo->Bar->object
当OldboyTeacher 自身调用某个属性时,
查找顺序为:OldboyTeacher->Foo->Bar->object->Mymeta->type
九、补充知识
调用对象其实就是在调用对象类中定义的绑定方法__call__
class Foo:
def __call__(self, *args, **kwargs):
print('================>')
print(self)
print(args)
print(kwargs)
obj1 = Foo()
obj1(1,2,3,a=1,b=2)