python中的metaclass
metaclass元类
python中一切皆对象,定义的类也是对象。
类的类型为元类,默认的元类为type。
class A(object):
pass
print(type(A))
output:
<class 'type'>
声明
声明定义元类需继承type
# MetaClass要继承type
class Meta(type):
pass
# 指定metaclass
class A(metaclass=Meta):
pass
print(type(A))
output:
<class '__main__.Meta'>
类的初始化
类是对象,也有初始化过程,它的初始化过程就是由它的元类定义。
class Meta(type):
def __new__(mcs, *args, **kwargs):
print('__new__')
print(mcs.__name__)
print(args)
print(kwargs)
print()
return super().__new__(mcs, *args, **kwargs)
def __init__(cls, classname, superclasses, attribute_dict):
super().__init__(classname, superclasses, attribute_dict)
print('__init__')
print(cls.__name__)
print(classname)
print(superclasses)
print(attribute_dict)
print()
def __call__(cls, *args, **kwargs):
print('__call__')
obj = cls.__new__(cls, *args, **kwargs)
obj.__init__(*args, **kwargs)
return obj
class Foo(object, metaclass=Meta):
pass
print('-----')
# 等价于
MyClass = Meta('MyClass', tuple([object]), {'__module__': '__main__', '__qualname__': 'MyClass'})
output:
__new__
Meta
('Foo', (<class 'object'>,), {'__module__': '__main__', '__qualname__': 'Foo'})
{}
__init__
Foo
Foo
(<class 'object'>,)
{'__module__': '__main__', '__qualname__': 'Foo'}
-----
__new__
Meta
('MyClass', (<class 'object'>,), {'__module__': '__main__', '__qualname__': 'MyClass'})
{}
__init__
MyClass
MyClass
(<class 'object'>,)
{'__module__': '__main__', '__qualname__': 'MyClass'}
定义类的等价代码
class Foo(object, metaclass=MyMeta):
pass
# 定义类相当于
MyClass = MyMeta('MyClass', tuple([object]), {'__module__': '__main__', '__qualname__': 'MyClass'})
metaclass的使用
在一些情况下,比如想要实现自定义__instancecheck__
来判断一个实例是否可迭代,即实现了__iter__
方法。
class Iterable(object):
def __instancecheck__(self, instance):
print('check!')
return hasattr(instance, '__iter__')# 如果有__iter__魔术方法,则返回真
ls = []
print(isinstance(ls, Iterable))
output:
<class 'type'>
发现并没有输出check,说明没有执行到__instancecheck__
,这并不符合我们的预期。
查看python的c语言源码
在abstract.c和typeobject.c
中找到:
int
PyObject_IsInstance(PyObject *inst, PyObject *cls)
{
PyThreadState *tstate = _PyThreadState_GET();
return object_recursive_isinstance(tstate, inst, cls);
}
static int
object_recursive_isinstance(PyThreadState *tstate, PyObject *inst, PyObject *cls)
{
_Py_IDENTIFIER(__instancecheck__);
if (Py_IS_TYPE(inst, (PyTypeObject *)cls)) {
...
}
if (PyType_CheckExact(cls)) {
...
}
if (PyTuple_Check(cls)) {
...
}
PyObject *checker = _PyObject_LookupSpecial(cls, &PyId___instancecheck__);
if (checker != NULL) {
//用户定义行为
}
else if (_PyErr_Occurred(tstate)) {
return -1;
}
return object_isinstance(inst, cls);
}
PyObject *
_PyObject_LookupSpecial(PyObject *self, _Py_Identifier *attrid)
{
PyObject *res;
//并没有对判断self是否有attrid,而是判断type(self),所以魔术方法要在元类中实现。
res = _PyType_LookupId(Py_TYPE(self), attrid);
if (res != NULL) {
descrgetfunc f;
if ((f = Py_TYPE(res)->tp_descr_get) == NULL)
Py_INCREF(res);
else
res = f(res, self, (PyObject *)(Py_TYPE(self)));
}
return res;
}
从代码中发现isinstance的判断是有多级的:
对于isinstance(v, C)
if type(v) == C: return true
if type(C)==type : return C in v.__class__.__mro__
if type(C) == tuple:...
if hasattr(type(C), '__instancecheck__'): return type(C).__instancecheck__(v)
即用户行为- 默认行为
可以看到用户定义的行为是比较靠后的。
所以要想实现我们预期的效果,需要越过前面的三项。
- 第一项,v不是C的实例即可
- 第三项,只要C不是tuple类型即可
第二项,要使得type©!=type,但一般情况下,我们定义的类都默认是type类型。
这时就要自定义元类了。
# 在查找__instancechect__函数时,查找的是type(C)而不是C
# 所以要在元类MetaIter中实现__instancecheck__而不是Iterable
class MetaIter(type):
def __instancecheck__(self, instance):
print('check!')
return hasattr(instance, '__iter__')
class Iterable(metaclass=MetaIter):
pass
ls = []
print(type(Iterable))
print(isinstance(ls, Iterable))
output:
<class '__main__.MetaIter'>
check!
True
这样就跟预期一致了。