python 元类理解
在理解元类之前,我们先要清楚python的PyClassObject对象
也就是我们常用的object对象,下面统一称为PyClassObject
在python面向对象开发中,自定义的python 对象一般都要求继承PyClassObject对象
In [10]: class A(object): pass
在《python对象的本质》这篇文章中,我们清楚,创建python对象,必须知道此对象的C语言结构体的定义,以及该对象所需的ob_type
下面是claasObject对象的C语言结构体:
# PyClassObject 的定义
typedef struct
{
PyObject_HEAD
PyObject *cl_bases;! ! /* A tuple of class objects */
PyObject *cl_dict;! ! /* A dictionary */
PyObject *cl_name;! ! /* A string */
PyObject *cl_getattr;
PyObject *cl_setattr;
PyObject *cl_delattr;
} PyClassObject;
In [10]: class A(object): pass
# type(A) == type 说明A对象的ob_type是type对象
In [11]: type(A)
Out[11]: type
既然我们清楚了PyClassObject对象是type对象创建的(或者可以说是实例化)
再深入一点,type对象如何实例化得到object对象的呢,我们还是回到type对象的结构体
typedef struct _typeobject {
.....
const char *tp_name; /* 打印 "<module>.<name>" */
initproc tp_init; // 定义了__init__操作
newfunc tp_new; // 定义了__new__操作
PyObject *tp_bases; // 保存所有的父类
PyObject *tp_mro; // 定义了查找父类的规则
.....
}
注意,type对象有new方法, type对象实例化得到PyClassObject对象就是调用type对象的new方法实现的
创建PyClassObject对象时查找new方法的规则如下:
1. 首先去查找PyClassObject对象对应的new方法
2. 如果继续PyClassObject没有定义,则查找ob_type指定的对象,也就是type对象的new方法
最终调用type对象的new方法实例化得到PyClassObject对象,实例化特别需要注意的参数是:
*cl_name: 定义类的名称 (string)
*cl_dict: 定义类的字段 (dict)
*cl_bases: 定义对象继承的父类 (tunple)
python的new方法比较复杂,我可以使用type
函数变相的调用type对象的new方法。
type
函数的调用如下:
# type(类名, 父类,字段)
# 创建了User类,并定义了User类的父类和字段
In [28]: User = type("User", (object, ), {"name" : "mike"})
# 打印User的name字段
In [29]: print User.name
mike
# 打印User的cl_name
In [30]: print User
<class '__main__.User'>
# 打印User的dict属性
In [53]: User.__dict__
Out[53]:
<dictproxy {'__dict__': <attribute '__dict__' of 'User' objects>,
'__doc__': None,
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'User' objects>,
'name': 'mike'}>
自此,我们使用type
函数就可以创建一个PyClassObject类
别忘了,除了type
函数,我们可以使用class
关键字去创建一个类
Class A(object): pass
使用class
关键字创建的类会自动调用type对象的new方法。
到目前我们,我们搞清楚了type对象是如何创建pyClassObject对象
type对象创建了pyClassObject对象,所以我们可以说type对象就是pyClassObject对象的元类。
现在我们清楚了元类的本质就是可以创建一个python pyClassObject类
pyClassObject.ob_type = type
, 所以,可以说对象属性ob_type指定的对象就是本对象的元类。
如果不想让type对象创建pyClassObject,那如何自定义一个类的元类呢
说白了,自定义一个元类就是改变这个类的obtype指向,在python中可以使用`_metaclass`改变ob_type的值向,同时元类必须有个new函数,用来创建一个类
下面举两个例子,说明元类的使用
创建一个字段的值都是大写的类
# 继承type类,调用type方法
class UpperAttrMetaClass(type):
def __new__(mcs, name, bases, attrs):
mcs.org_attrs = attrs
new_attrs = dict((name.upper(), value) for name, value in attrs)
return type.__new__(mcs, name, bases, new_attrs)
class User(object):
__metaclass__ = UpperAttrMetaClass
name = "sunny"
改变类方法的返回值
def return_self(func):
@wraps(func)
def wrapped(self, *args, **kwargs):
func(self, *args, **kwargs)
return self
return wrapped
class ReturnSelfMeta(type):
def __new__(mcs, name, bases, attrs): # noqa
cls = super(ReturnSelfMeta, mcs).__new__(mcs, name, bases, attrs)
methods = cls.__methods_return_self__
for method in methods:
origin = getattr(cls, method)
setattr(cls, method, return_self(origin))
return cls
什么情况下需要使用元类?
如果不需要改变类类的属性或者拦截类的方法,都是有使用元类