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

什么情况下需要使用元类?

如果不需要改变类类的属性或者拦截类的方法,都是有使用元类

results matching ""

    No results matching ""