python元类的元类_深刻理解Python中的元类(meta_class)

《what-is-a-metaclass-in-python》真的是非常棒(注意,本文中专指e-satis的回答),仔细阅读完这篇文章,基本上就搞清了metaclass

everything is object

python中,一切都是对象,比如一个数字、一个字符串、一个函数。对象是类(class)的是实例,类(class)也是对象,是type的实例。type对象本身又是type类的实例(鸡生蛋还是蛋生鸡?),因此我们称type为metaclass(中文元类)。在《python源码剖析》中,有清晰的表示

在python中,可以通过对象的__class__属性来查看对应的类,也可以通过isinstance来判断一个对象是不是某一个类的实例。例如:In [1]: class Obj(object):

...:     a = 1

...:

In [2]: o = Obj()

...: print(o.__class__)

...:

In [3]: print(isinstance(o, Obj))

True

In [4]: print(Obj.__class__)

In [5]: isinstance(Obj,type)

Out[5]: True

In [6]: type.__class__

Out[6]: type

metaclass可以定制类的创建

我们都是通过class OBJ(obejct):pass的方式来创建一个类,上面有提到,类(class)是type类型的实例,按照我们常见的创建类的实例(instance)的方法,那么类(class)应该就是用 class="type"(*args)的方式创建的。确实如此,python document中有明确描述:class type(name, bases, dict)

With three arguments, return a new type object. This is essentially a dynamic form of the class statement.

The name string is the class name and becomes the __name__ attribute;

the bases tuple itemizes the base classes and becomes the __bases__ attribute;

and the dict dictionary is the namespace containing definitions for class body and becomes the __dict__ attribute.

For example, the following two statements create identical type objects:

该函数返回的就是一个class,三个参数分别是类名、基类列表、类的属性。比如在上面提到的OBJ类,完全等价于:Obj = type('OBJ', (), {'a': 1})

当然,用上面的方式创建一个类(class)看起来很傻,不过其好处在于可以动态的创建一个类。

python将定制类开放给了开发者,type也是一个类型,那么自然可以被继承,type的子类替代了Python默认的创建类(class)的行为,已探索的一些想法包括日志记录,接口检查,自动委托,自动属性创建,代理,框架和自动资源锁定/同步。

那么当我们用class Obj(obejct):pass的形式声明一个类的时候,怎么指定Obj的创建行为呢,那就是在类中使用__metaclass__。最简单的例子:class Metaclass(type):

def __new__(cls, name, bases, dct):

print 'HAHAHA'

dct['a'] = 1

return type.__new__(cls, name, bases, dct)

print 'before Create Obj'

class Obj(object):

__metaclass__ = Metaclass

print 'after Create Obj'

if __name__ == '__main__':

print Obj.a

运行结果:

before Create OBJ

HAHAHA

after Create OBJ

1

可以看到在代码执行的时候,在创建Obj这个类的时候,__metaclass__起了作用,为OBJ增加了一个类属性‘a'

关于__metaclass__的两个细节

首先,__metaclass__是一个callable即可,不一定非得是一个类,在what-is-a-metaclass-in-python就有__metaclass__是function的实例,也解释了为什么__metaclass__为一个类是更好的选择。

其次,就是如何查找并应用__metaclass__,python document中是有的:The appropriate metaclass is determined by the following precedence rules:

● If dict['__metaclass__'] exists, it is used.

● Otherwise, if there is at least one base class, its metaclass is used (this looks for a __class__ attribute first and if not found, uses its type).

● Otherwise, if a global variable named __metaclass__ exists, it is used.

● Otherwise, the old-style, classic metaclass (types.ClassType) is used.

意思是先从类的dict中查找,否则从基类的dict查找(这里会有一些需要注意的细节,后文会提到),否则从global作用域查找,否则使用默认的创建方式

python源码在ceval.c::build_class,核心代码如下static PyObject *

build_class(PyObject *methods, PyObject *bases, PyObject *name)

{

PyObject *metaclass = NULL, *result, *base;

if (PyDict_Check(methods))

metaclass = PyDict_GetItemString(methods, "__metaclass__");

if (metaclass != NULL)

Py_INCREF(metaclass);

else if (PyTuple_Check(bases) && PyTuple_GET_SIZE(bases) > 0) {

base = PyTuple_GET_ITEM(bases, 0);

metaclass = PyObject_GetAttrString(base, "__class__");

if (metaclass == NULL) {

PyErr_Clear();

metaclass = (PyObject *)base->ob_type;

Py_INCREF(metaclass);

}

}

else {

PyObject *g = PyEval_GetGlobals();

if (g != NULL && PyDict_Check(g))

metaclass = PyDict_GetItemString(g, "__metaclass__");

if (metaclass == NULL)

metaclass = (PyObject *) &PyClass_Type;

Py_INCREF(metaclass);

}

result = PyObject_CallFunctionObjArgs(metaclass, name, bases, methods,

NULL);

Py_DECREF(metaclass);

if (result == NULL && PyErr_ExceptionMatches(PyExc_TypeError)) {

/* A type error here likely means that the user passed

in a base that was not a class (such the random module

instead of the random.random type).  Help them out with

by augmenting the error message with more information.*/

PyObject *ptype, *pvalue, *ptraceback;

PyErr_Fetch(&ptype, &pvalue, &ptraceback);

if (PyString_Check(pvalue)) {

PyObject *newmsg;

newmsg = PyString_FromFormat(

"Error when calling the metaclass bases"

"    %s",

PyString_AS_STRING(pvalue));

if (newmsg != NULL) {

Py_DECREF(pvalue);

pvalue = newmsg;

}

}

PyErr_Restore(ptype, pvalue, ptraceback);

}

return result;

}

ceval::build_class

https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值