#90 Python 源码学习 05: strbytes

2021-07-28

有了 int 的一点点经验,先找:

SETBUILTIN("bytearray",             &PyByteArray_Type);
SETBUILTIN("bytes",                 &PyBytes_Type);
SETBUILTIN("str",                   &PyUnicode_Type);

INIT_TYPE(&PyByteArray_Type, "bytearray");
INIT_TYPE(&PyBytes_Type, "str");
INIT_TYPE(&PyUnicode_Type, "str");

PS: 而且还可以通过 bytes 类型的 __doc__ 内容在 cpython 源码中搜索。
PS: 这里有一个奇怪的地方,就是 INIT_TYPE 的时候,把 PyBytes_Type 给了 str

PyBytes_Type

Objects/bytesobject.c

PyTypeObject PyBytes_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "bytes",
    PyBytesObject_SIZE,
    sizeof(char),
    0,                                          /* tp_dealloc */
    0,                                          /* tp_vectorcall_offset */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_as_async */
    (reprfunc)bytes_repr,                       /* tp_repr */
    &bytes_as_number,                           /* tp_as_number */
    &bytes_as_sequence,                         /* tp_as_sequence */
    &bytes_as_mapping,                          /* tp_as_mapping */
    (hashfunc)bytes_hash,                       /* tp_hash */
    0,                                          /* tp_call */
    bytes_str,                                  /* tp_str */
    PyObject_GenericGetAttr,                    /* tp_getattro */
    0,                                          /* tp_setattro */
    &bytes_as_buffer,                           /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
        Py_TPFLAGS_BYTES_SUBCLASS,              /* tp_flags */
    bytes_doc,                                  /* tp_doc */
    0,                                          /* tp_traverse */
    0,                                          /* tp_clear */
    (richcmpfunc)bytes_richcompare,             /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    bytes_iter,                                 /* tp_iter */
    0,                                          /* tp_iternext */
    bytes_methods,                              /* tp_methods */
    0,                                          /* tp_members */
    0,                                          /* tp_getset */
    &PyBaseObject_Type,                         /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    0,                                          /* tp_init */
    0,                                          /* tp_alloc */
    bytes_new,                                  /* tp_new */
    PyObject_Del,                               /* tp_free */
};
typedef struct {
    PyObject_VAR_HEAD
    Py_hash_t ob_shash;
    char ob_sval[1];

    /* Invariants:
     *     ob_sval contains space for 'ob_size+1' elements.
     *     ob_sval[ob_size] == 0.
     *     ob_shash is the hash of the string or -1 if not computed yet.
     */
} PyBytesObject;

我不知道这个 PyBytesObject 是个啥。
回头找 int 相关信息,果然找到 typedef struct _longobject PyLongObject;
就在网上搜索,结果找到 python 官网(Bytes Objects)有相关信息:

PyBytesObject

This subtype of PyObject represents a Python bytes object.

PyTypeObject PyBytes_Type

This instance of PyTypeObject represents the Python bytes type; it is the same object as bytes in the Python layer.

PyUnicode_Type

Objects/unicodeobject.c

PyTypeObject PyUnicode_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "str",                        /* tp_name */
    sizeof(PyUnicodeObject),      /* tp_basicsize */
    0,                            /* tp_itemsize */
    /* Slots */
    (destructor)unicode_dealloc,  /* tp_dealloc */
    0,                            /* tp_vectorcall_offset */
    0,                            /* tp_getattr */
    0,                            /* tp_setattr */
    0,                            /* tp_as_async */
    unicode_repr,                 /* tp_repr */
    &unicode_as_number,           /* tp_as_number */
    &unicode_as_sequence,         /* tp_as_sequence */
    &unicode_as_mapping,          /* tp_as_mapping */
    (hashfunc) unicode_hash,      /* tp_hash*/
    0,                            /* tp_call*/
    (reprfunc) unicode_str,       /* tp_str */
    PyObject_GenericGetAttr,      /* tp_getattro */
    0,                            /* tp_setattro */
    0,                            /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
    Py_TPFLAGS_UNICODE_SUBCLASS,   /* tp_flags */
    unicode_doc,                  /* tp_doc */
    0,                            /* tp_traverse */
    0,                            /* tp_clear */
    PyUnicode_RichCompare,        /* tp_richcompare */
    0,                            /* tp_weaklistoffset */
    unicode_iter,                 /* tp_iter */
    0,                            /* tp_iternext */
    unicode_methods,              /* tp_methods */
    0,                            /* tp_members */
    0,                            /* tp_getset */
    &PyBaseObject_Type,           /* tp_base */
    0,                            /* tp_dict */
    0,                            /* tp_descr_get */
    0,                            /* tp_descr_set */
    0,                            /* tp_dictoffset */
    0,                            /* tp_init */
    0,                            /* tp_alloc */
    unicode_new,                  /* tp_new */
    PyObject_Del,                 /* tp_free */
};

字符串格式化

我想找 % 格式化,{} 格式化,以及最新的 fstring 的实现方式,但是代码的复杂性,让我在预计的时间之内无法完成,只好到此打住。

可能得从 parse.cast.c 语法树开始,再到 parse_string.c

// Objects/unicodeobject.c
// PyMethodDef unicode_methods 中的几行:
{"format", (PyCFunction)(void(*)(void)) do_string_format, METH_VARARGS | METH_KEYWORDS, format__doc__},
{"format_map", (PyCFunction) do_string_format_map, METH_O, format_map__doc__},
UNICODE___FORMAT___METHODDEF

// Objects/clinic/unicodeobject.c.h
#define UNICODE___FORMAT___METHODDEF    \
    {"__format__", (PyCFunction)unicode___format__, METH_O, unicode___format____doc__},

static PyObject *
unicode___format__(PyObject *self, PyObject *arg)
{
    PyObject *return_value = NULL;
    PyObject *format_spec;

    if (!PyUnicode_Check(arg)) {
        _PyArg_BadArgument("__format__", "argument", "str", arg);
        goto exit;
    }
    if (PyUnicode_READY(arg) == -1) {
        goto exit;
    }
    format_spec = arg;
    return_value = unicode___format___impl(self, format_spec);

exit:
    return return_value;
}
static PyObject *
do_string_format(PyObject *self, PyObject *args, PyObject *kwargs)
{
    SubString input;

    /* PEP 3101 says only 2 levels, so that
       "{0:{1}}".format('abc', 's')            # works
       "{0:{1:{2}}}".format('abc', 's', '')    # fails
    */
    int recursion_depth = 2;

    AutoNumber auto_number;

    if (PyUnicode_READY(self) == -1)
        return NULL;

    AutoNumber_Init(&auto_number);
    SubString_init(&input, self, 0, PyUnicode_GET_LENGTH(self));
    return build_string(&input, args, kwargs, recursion_depth, &auto_number);
}

static PyObject *
do_string_format_map(PyObject *self, PyObject *obj)
{
    return do_string_format(self, NULL, obj);
}

没啥头绪,放弃。

#89 Python 源码学习 04: int

2021-07-20

经过源码分析,可以得知,所有类型的定义都是通过 object.c 中的 INIT_TYPE 语句,比如 INIT_TYPE(&PyLong_Type, "int");

然后,可能是通过 SETBUILTIN("int", &PyLong_Type); 设置成内置方法。

int 类型最后就指向了一个叫做 PyLong_TypePyTypeObject 类型变量。

PyTypeObject

Objects/longobject.c

PyTypeObject PyLong_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "int",                                      /* tp_name */
    offsetof(PyLongObject, ob_digit),           /* tp_basicsize */
    sizeof(digit),                              /* tp_itemsize */
    0,                                          /* tp_dealloc */
    0,                                          /* tp_vectorcall_offset */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_as_async */
    long_to_decimal_string,                     /* tp_repr */
    &long_as_number,                            /* tp_as_number */
    0,                                          /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    (hashfunc)long_hash,                        /* tp_hash */
    0,                                          /* tp_call */
    0,                                          /* tp_str */
    PyObject_GenericGetAttr,                    /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
        Py_TPFLAGS_LONG_SUBCLASS,               /* tp_flags */
    long_doc,                                   /* tp_doc */
    0,                                          /* tp_traverse */
    0,                                          /* tp_clear */
    long_richcompare,                           /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    0,                                          /* tp_iter */
    0,                                          /* tp_iternext */
    long_methods,                               /* tp_methods */
    0,                                          /* tp_members */
    long_getset,                                /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    0,                                          /* tp_init */
    0,                                          /* tp_alloc */
    long_new,                                   /* tp_new */
    PyObject_Del,                               /* tp_free */
};

其中所有的方法就在 PyMethodDef long_methodsPyNumberMethods long_as_number 中。
尤其是 long_as_number 可能就是那些重载操作符的基础。

long_newlong_new_impl

可能是 int 方法对应的实现。

#86 Python 解释器

2021-06-21

除了 CPython 之外的几种比较知名的 Python 实现:

  1. PyPy
  2. Jython 原名 JPython,原作者停止维护之后,社区将项目改名并继续维护。
  3. IronPython 运行在 .NET 平台
    之前由微软开发(姊妹项目 IronRuby),后来放弃。项目由志愿者维护。
  4. Pyston Dropbox 停止资助该项目,不过还活着,在继续发版。
  5. Unladen Swallow 谷歌的项目,看介绍,思路和 Pyston 差不多(在 pyston 前面),早死翘翘了
  6. Stackless Python: greenlet 就是从这个项目中弄出来的一个成果。
  7. MicroPython: 专门运行在单片机上(有一个 fork 叫 CircuitPython,似乎也有点名气)

#85 常见的 Python 性能提升手段

2021-06-21

Python 本身的性能现在还很有待提升,但是为什么没有阻碍它的推广呢?

因为 Python 常常被当作是一种胶水语言,它有很好的与 C 互操作性,一直维护与 C 库对接的 API,也就是说很容易可以通过 C 拓展来提升性能。

新的运行时

  • PyPy 据说能有很大提升
  • Pyston LLVM 编译器架构 + JIT
    https://github.com/pyston/pyston
    注意:Dropbox 放弃了这个项目,转向 Go 语言,但这个项目还在继续开发中, 比较活跃
  • Psyco http://psyco.sourceforge.net/ 已经没有维护了,据说只维护到 Python 2.4
    官方推荐 PyPy

局部优化

C/C++ 绑定

  • SWIG: C/C++ 写的代码自动绑定到 Python,就是说生成一个 Python 可以调用的 .so 模块
  • pybind11
  • Boost.Python

直接调用 C

直接开发 CPython 拓展模块(include <Python.h>)也可以,但我不觉得这是一个好办法。

GPU

  • GPULib
  • PyStream
  • PyCUDA
  • PyOpenCL

其他

  • F2PY: Fortran to Python, 将 Fortran 代码自动绑定到 Python, NumPy 的一部分
    也是编译成 .so 包
    我不清楚相关应用场景,以及这个方案是否有性能上的优势

参考资料与拓展阅读

#82 Python 源码学习 02: PyObject

2021-06-19

源码

Include/object.h

/* Nothing is actually declared to be a PyObject, but every pointer to
 * a Python object can be cast to a PyObject*.  This is inheritance built
 * by hand.  Similarly every pointer to a variable-size Python object can,
 * in addition, be cast to PyVarObject*.
 */
typedef struct _object {
    _PyObject_HEAD_EXTRA   // 如果开启了 Py_TRACE_REFS 增加一个 _ob_next, _ob_prev
                           // 使 all live heap objects 组成一个双向链表
    Py_ssize_t ob_refcnt;  // 长整型
    PyTypeObject *ob_type;
} PyObject;

/* Cast argument to PyObject* type. */
#define _PyObject_CAST(op) ((PyObject*)(op))
#define _PyObject_CAST_CONST(op) ((const PyObject*)(op))

typedef struct {
    PyObject ob_base;
    Py_ssize_t ob_size; /* Number of items in variable part */
} PyVarObject;

/* Cast argument to PyVarObject* type. */
#define _PyVarObject_CAST(op) ((PyVarObject*)(op))

#define Py_REFCNT(ob)           (_PyObject_CAST(ob)->ob_refcnt)
#define Py_TYPE(ob)             (_PyObject_CAST(ob)->ob_type)
#define Py_SIZE(ob)             (_PyVarObject_CAST(ob)->ob_size)

PyObject

相当于所有 Python 对象的父类,包含类型,引用计数等信息。

注释说的很清楚,不会有直接声明的 PyObject 变量,只会有 PyObject* 指针,所有指向 Python 对象的指针都可以转换成 PyObject*

PyVarObject

表示 ob_size 个 PyObject,也就是说 PyVarObject 是一个 PyObject 的容器。

#81 NumPy 基础

2021-06-18

NumPy 提供了一个高效的数据结构(数组/矩阵)及对应运算支持,据说效率和 C 接近,是 Python 科学计算生态的基础。