元类(一切皆对象)以及深浅拷贝
元类
元类的来源是:python中一切皆对象。
什么是元类
元类就是用来实例化产生类的类
关系:元类---实例化---类(自定义的类)---实例化----对象(obj)
如何查看内置的元类
1.type是内置的元类
2.我们用class关键字定义出来的所有类以及内置的类都是由内置的元类type实例化产生的
例如:在python中int、dict内置元类都继承自object类,int和dict又都是type元类的对象
print(type(int)) # <class 'type'>
print(type(dict)) # <class 'type'>
那么type和object又是什么关系呢?我们来type一下object和type!
print(type(type)) #<class 'type'>
print(type(object)) #<class 'type'>
其实:
1.object的元类其实是type类,object是由type类构造出来的对象
2.type是自己的对象(指针指向了自己)
3.type类又继承了object类
class机制分析(class如何造出类的)
calss其实底层执行了以下四个步骤,造出了类!
class Func:
def put(self):
print('正在执行上传功能')
def __del__(self):
print('run...')
# 1.得到类名
class_name = 'Func'
# 2.得到类的基类
class_bases = (object,)
# 3.执行类体代码拿到名称空间!
class_dict = {}
class_body = """
def put(self):
print('正在执行上传功能')
def __del__(self):
print('run...')
"""
# exec第一个参数是类体代码、第二个是类体代码中的全局变量、第三的是一个空字典容器
exec (class_body,{},class_dict)
print(class_dict)
# 4.调用元类,得到People类
Func = type(class_name,class_bases,class_dict)
print(Func)
# Func类就是type元类实例化产生出来的对象!!!!
如何自定义元类来控制类的产生
在3.3中,我们是使用type元类控制Func类的产生。其实,我们也可以自定义元类来控制类产生
class MyMeta(type): # 只有继承了type的类才是元类
def __init__(self,x,y,z): # 注意调用MyMeta这个类其实传入了四个参数分别是self、class_name,class_bases,class_dict
print('run...')
print(x) # x对应class_name
print(y) # y对应class_bases
print(z) # z对应class_dict
class Func(metaclass=MyMeta):
def put(self):
print('正在执行上传功能')
def __del__(self):
print('run...')
# 在自定义类的时候,metaclass默认等于type,我们可以通过指定metaclass=MyMeta来自定义元类
# 指定了metaclass=MyMeta,其实就执行了第四步调用元类Func = MyMeta(class_name,class_bases,class_dict)
# 调用MyMeta(class_name,class_bases,class_dict)发生了三件事!
# 注意!!!调用它就等于调用了type的__call__方法!!!!
# 1.先造一个空对象---Func--这里其实先调用了MyMeta类里的__new__()方法
# 2.调用MyMeta这个类的__init__方法,完成初始化对象操作
# 3.返回初始化好的对象
"""
完成上述操作之后,我们就可以在自定义的MyMeta元类里面的init方法里面,规定一下类的产生必须满足那些条件!
"""
元类下的属性查找
首先,切记!!父类不是元类!!
对象.属性查找是先从自己那找,再到类中,再到该类的父类中,最后到object类
类.属性查找是先从该类的父类中找,再到父类的父类中找,再到object中找,最后还要到该类的元类中找
一切皆对象的好处
它可以通过一切皆对象,使得任何类型的变量值能够赋值给变量!
因为,其本质是变量都指向了一个内存地址,而并不是直接指向具体的数据(在其他语言中不能这样)
深浅拷贝
1.赋值,本质是多了一个变量指向另一个变量的内存地址
l = [11,22,[311,444,55]]
l2 = l # 赋值操作,本质就是l2指向了l的内存地址
print(l2 is l) # 结果为true
2.浅拷贝,本质是又拷贝一份内存地址指向对应的变量值
from copy import copy
l = [111,222,[333,444]]
l3 = copy(l) # 浅拷贝,只是拷贝了l的内存地址
print(l3 is l) # 结果为false
# 修改l3的列表值,l也会改变!
3.深拷贝:本质内存地址、值全部拷贝一份,非常占内存!
from copy import deepcopy
l = [111,222,[333,444]]
l4 = deepcopy(l)
print(l4 is l) # 结果为false
# 深拷贝是把内存地址、变量值全部拷贝了一份,所以修改l4中列表的值,l并不会改变
# 注意:深拷贝,拷贝的时候l列表里面的不可变类型地址并不会改变,改变的是可变类型的地址!!