python的final class

2017, Dec 04    

本文介绍了python中的final class,以及如何实现自己的final class。在java中,当一个类前面加上final后,表面这个类不希望被继承。python中并没有final这样的关键字,那python中是否也有final class呢?我们怎么实现自己的final class呢?

Python自带的final class

我们可以试运行一下下边的代码

g = iter('ab')
print(type(g))

class MyIter(type(g)):
    pass

i = MyIter()

这段代码中,希望继承str_iterator,实现自己的迭代器。运行后的结果:

输出:
<class 'str_iterator'>
Traceback (most recent call last):
  File "/home/rainman/test.py", line 9, in <module>
    class MyIter(type(g)):
TypeError: type 'str_iterator' is not an acceptable base type

看来str_iterator不运行被继承。这个错误试怎么发生的呢,python是怎么判断这个类不能被继承呢。

python类定义的tp_flags字段

查看python的C实现,可以找到两处会抛出这个异常的地方:

typeobject.c:

static PyTypeObject *
best_base(PyObject *bases)
{
    ...

    for (i = 0; i < n; i++) {
        ...

        if (!PyType_HasFeature(base_i, Py_TPFLAGS_BASETYPE)) {
            PyErr_Format(PyExc_TypeError,
                         "type '%.100s' is not an acceptable base type",
                         base_i->tp_name);
            return NULL;
        }

        ...
    }
    assert (base != NULL);

    return base;
}

object.h:

#define PyType_HasFeature(t,f)  (((t)->tp_flags & (f)) != 0)

/* Set if the type allows subclassing */
#define Py_TPFLAGS_BASETYPE (1UL << 10)

重点在”PyType_HasFeature”,它会查看类型的tp_flags字段,是否设置了“Py_TPFLAGS_BASETYPE”。而“Py_TPFLAGS_BASETYPE”正是用来标志一个类是否能够被继承。

str_iterator类定义

查看str_iterator的类定义,确实没有设置标志“Py_TPFLAGS_BASETYPE”

PyTypeObject PyUnicodeIter_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "str_iterator",         /* tp_name */
    sizeof(unicodeiterobject),      /* tp_basicsize */

    ...

    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */

    ...
};

实现自己的final class

如果是通过C语言来实现,当然可以参照python内部的final class。但如果纯python代码的方式来实现呢? 我们知道python的metaclass可以控制生成类,参照python的abstract class,我们应该也可以实现类似的功能。例如:

class FinalMeta(type):

    def __new__(mcls, name, bases, dict):
        for base in bases:
            if isinstance(base, FinalMeta):
                raise TypeError("type '{0}' is not an acceptable base type".format(base.__name__))
        cls = super().__new__(mcls, name, bases, dict)
        return cls

class Parent(dict, metaclass=FinalMeta):
    pass

class Child(Parent):
    pass

输出:
Traceback (most recent call last):
  File "/home/rainman/test.py", line 18, in <module>
    class Child(Parent):
  File "/home/rainman/test.py", line 11, in __new__
    raise TypeError("type '{0}' is not an acceptable base type".format(base.__name__))
TypeError: type 'Parent' is not an acceptable base type

可以定义个叫FinalMeta的metaclass,在__new__方法生成类时,判断类的基类中,是否有类型为FinalMeta的类。即通过isintance判断类的类型是否是FinalMeta。