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)

  1. if type(v) == C: return true
  2. if type(C)==type : return C in v.__class__.__mro__
  3. if type(C) == tuple:...
  4. if hasattr(type(C), '__instancecheck__'): return type(C).__instancecheck__(v)即用户行为
  5. 默认行为

可以看到用户定义的行为是比较靠后的。

所以要想实现我们预期的效果,需要越过前面的三项。

  • 第一项,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

这样就跟预期一致了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值