元类
元类
什么是元类?
要知道什么是元类就必须要了解一个大前提:python中所有的都是对象
在知道这个前提下我们就可以知道类也是一个对象 Σ(⊙▽⊙"a
那么一个对象就会有一个类来负责创建它
我们就把创建 “类”(实际也是一个对象)的 “类” 叫做——元类
怎么找到元类呢?
我们可以使用 type 方法来打印对象的类型
那么我们是不是也可以用 type 来打印类的数据类型呢?
print(type(list)) # <class 'type'>
print(type(str)) # <class 'type'>
print(type(object)) # <class 'type'>
就连所有的类的基类都是object类都是由元类所创建的
发现所有的类都是由 type 创建的。
??type??这不是经常用的一个内置方法吗?
怎么又变成了创建类的类了???
虽然我们看不到它的源码,但是它很知趣的给了我们大概的解释:
它可以传递一个参数或者传递三个参数:
- 一个参数:对象的类型(来自百度翻译)
- 三个参数:一个新对象(来自百度翻译)
原来它真的就是可以用来创建对象。孤陋寡闻,孤陋寡闻。
创建类的参数的含义
- object_or_name :类名
- bases : 继承的父类(元祖的形式传递)
- dict :类的命名空间(所有的属性与方法)
使用type创建一个类
既然我们已经知道type怎么用了那么我们就用这个 “马良笔” 来手动的创建一个类
def set_name(self,name):
self.name = name
def get_name(self):
return self.name
# 手动创建一个类
Custom = type('Custom',(object,),{'set_name': set_name, 'get_name': get_name})
# 实例化Custom类
obj = Custom()
obj.set_name('gredae')
print(obj.get_name()) # gredae
个人感觉class会不会也是重写了__enter__和__exit__方法来实现一个类的信息获取,再使用exce函数将类的名称空间里的所有属性与方法给写成一个字典,这样类的名称,类继承的所有父类,与类的属性与方法就全部都有了,再在底层调用type来生成类。。
自定义一个元类
type 好像是用C语言写的。嗯~算了,从入门到入坟。。。
我们换一种思路吧!我们直接继承 type 然后对 type 的方法重写来实现自定义一个元类,嗯~可行性很高。
class Mytype(type):
pass
Custom = type('Custom',(object,),{'set_name': set_name, 'get_name': get_name})
print(type(Custom)) # <class '__main__.Mytype'>
好了,我们实现了自定义元类,好像还没有重写它的方法。。
重写方法之前要知道实例化一个类经过了那些步骤
- 前面我们说到类也是对象,是由 type 创建的,那么我们将类实例化的时候是这样的
list()
当我们这样实例化类的时候就会调用 type 的 __call__ 方法 - 而后在 __call__ 方法里面先使用 __new__ 创建一个空白的对象,什么都没有的对象。
- 然后使用新创建的这个对象来调用该类的 __init__ 来初始化这个对象
- 最后再返回该对象。这样才能实例化一个类。
虽然上面的步骤很多,但是实际实现起来也很多。嗯,没错。
我们来实现自定义元类:
class Mytype(type):
def __call__(self, *args, **kwargs):
obj = object.__new__(self)
obj.__init__(*args, **kwargs)
return obj
class Custom(metaclass=Mytype):
def set_name(self, name):
self.name = name
def get_name(self):
return self.name
f = Custom()
print(type(f)) # <class '__main__.Custom'>
print(type(Custom)) # <class '__main__.Mytype'>
f.set_name('gredae')
print(f.get_name()) # gredae
终于一个功能齐全的自定义元类完成了。。
属性查找
最后再来说下类的属性查找和对象的属性查找
- 类:先从类本身中找 -> mro继承关系去父类中找 -> 去自己定义的元类中找 -> type中 -> 报错
- 对象:先从对象自身找 -> 类中找 -> mro继承关系去父类中找 -> 报错