深入理解Python虚拟机中复数(complex)的实现原理及源码剖析

 更新时间:2023年03月14日 09:11:16   作者:一无是处的研究僧  
在本篇文章当中主要给大家介绍在 cpython 虚拟机当中是如何实现 复数 complex 这个数据类型的,这个数据类型在 cpython 当中一应该是一个算比较简单的数据类型了,非常容易理解

复数数据结构

在 cpython 当中对于复数的数据结构实现如下所示:

typedef struct {
    double real;
    double imag;
} Py_complex;
#define PyObject_HEAD                   PyObject ob_base;
typedef struct {
    PyObject_HEAD
    Py_complex cval;
} PyComplexObject;
typedef struct _object {
    _PyObject_HEAD_EXTRA
    Py_ssize_t ob_refcnt;
    struct _typeobject *ob_type;
} PyObject;

上面的数据结构图示如下:

复数的数据在整个 cpython 虚拟机当中来说应该算是比较简单的了,除了一个 PyObject 头部之外就是实部和虚部了。

  • ob_refcnt,表示对象的引用记数的个数,这个对于垃圾回收很有用处,后面我们分析虚拟机中垃圾回收部分在深入分析。
  • ob_type,表示这个对象的数据类型是什么,在 python 当中有时候需要对数据的数据类型进行判断比如 isinstance, type 这两个关键字就会使用到这个字段。
  • real,表示复数的实部。
  • imag,表示复数的虚部。

复数的操作

复数加法

下面是 cpython 当中对于复数加法的实现,为了简洁删除了部分无用代码。

static PyObject *
complex_add(PyObject *v, PyObject *w)
{
    Py_complex result;
    Py_complex a, b;
    TO_COMPLEX(v, a); // TO_COMPLEX 这个宏的作用就是将一个 PyComplexObject 中的 Py_complex 对象存储到 a 当中
    TO_COMPLEX(w, b);
    result = _Py_c_sum(a, b); // 这个函数的具体实现在下方
    return PyComplex_FromCComplex(result); // 这个函数的具体实现在下方
}
 
// 真正实现复数加法的函数
Py_complex
_Py_c_sum(Py_complex a, Py_complex b)
{
    Py_complex r;
    r.real = a.real + b.real;
    r.imag = a.imag + b.imag;
    return r;
}
 
PyObject *
PyComplex_FromCComplex(Py_complex cval)
{
    PyComplexObject *op;
 
    /* Inline PyObject_New */
    // 申请内存空间
    op = (PyComplexObject *) PyObject_MALLOC(sizeof(PyComplexObject));
    if (op == NULL)
        return PyErr_NoMemory();
    // 将这个对象的引用计数设置成 1
    (void)PyObject_INIT(op, &PyComplex_Type);
    // 将复数结构体保存下来
    op->cval = cval;
    return (PyObject *) op;
}

上面代码的整体过程比较简单:

  • 首先先从 PyComplexObject 提取真正的复数部分。
  • 将提取到的两个复数进行相加操作。
  • 根据得到的结果在创建一个 PyComplexObject 对象,并且将这个对象返回。

复数取反

复数取反操作就是将实部和虚部取相反数就可以了,这个操作也比较简单。

static PyObject *
complex_neg(PyComplexObject *v)
{
    Py_complex neg;
    neg.real = -v->cval.real;
    neg.imag = -v->cval.imag;
    return PyComplex_FromCComplex(neg);
}
 
PyObject *
PyComplex_FromCComplex(Py_complex cval)
{
    PyComplexObject *op;
 
    /* Inline PyObject_New */
    op = (PyComplexObject *) PyObject_MALLOC(sizeof(PyComplexObject));
    if (op == NULL)
        return PyErr_NoMemory();
    (void)PyObject_INIT(op, &PyComplex_Type);
    op->cval = cval;
    return (PyObject *) op;
}

Repr 函数

我们现在来介绍一下一个有趣的方法,就是复数类型的 repr 函数,这个和类的 __repr__ 函数是作用是一样的我们看一下复数的输出是什么:

>>> data = complex(0, 1)
>>> data
1j
>>> data = complex(1, 1)
>>> data
(1+1j)
>>> print(data)
(1+1j)

复数的 repr 对应的 C 函数如下所示:

static PyObject *
complex_repr(PyComplexObject *v)
{
    int precision = 0;
    char format_code = 'r';
    PyObject *result = NULL;
 
    /* If these are non-NULL, they'll need to be freed. */
    char *pre = NULL;
    char *im = NULL;
 
    /* These do not need to be freed. re is either an alias
       for pre or a pointer to a constant.  lead and tail
       are pointers to constants. */
    char *re = NULL;
    char *lead = "";
    char *tail = "";
    // 对应实部等于 0 虚部大于 0 的情况
    if (v->cval.real == 0. && copysign(1.0, v->cval.real)==1.0) {
        /* Real part is +0: just output the imaginary part and do not
           include parens. */
        re = "";
        im = PyOS_double_to_string(v->cval.imag, format_code,
                                   precision, 0, NULL);
        if (!im) {
            PyErr_NoMemory();
            goto done;
        }
    } else {
        /* Format imaginary part with sign, real part without. Include
           parens in the result. */
        // 将实部浮点数变成字符串
        pre = PyOS_double_to_string(v->cval.real, format_code,
                                    precision, 0, NULL);
        if (!pre) {
            PyErr_NoMemory();
            goto done;
        }
        re = pre;
        // 将虚部浮点数变成字符串
        im = PyOS_double_to_string(v->cval.imag, format_code,
                                   precision, Py_DTSF_SIGN, NULL);
        if (!im) {
            PyErr_NoMemory();
            goto done;
        }
        // 用什么括号包围起来
        lead = "(";
        tail = ")";
    }
    result = PyUnicode_FromFormat("%s%s%sj%s", lead, re, im, tail);
  done:
    PyMem_Free(im);
    PyMem_Free(pre);
 
    return result;
}

我们现在修改源程序将上面的 () 两个括号变成 [],编译之后执行的结果如下所示:

可以看到括号变成了 [] 。

总结

在本篇文章当中主要给大家介绍了在 cpython 虚拟机当中对于复数这一类型的数据结构以及他的具体实现。总体来说这个数据结构比较简单,操作也相对容易,比较容易理解,最后简单介绍了一下复数类型的 repr 实现,其实这个函数和 python 的类型系统有关,目前我们还没有仔细去讨论这一点,在后续的文章当中我们将深入的去学习这个知识点,现在我们就先了解其中部分函数即可。

到此这篇关于深入理解Python虚拟机中复数(complex)的实现原理及源码剖析的文章就介绍到这了,更多相关Python虚拟机 复数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Python语法分析之字符串格式化

    Python语法分析之字符串格式化

    这篇文章主要给大家介绍了关于Python语法分析之字符串格式化的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Python具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-06-06
  • python命令 -u参数用法解析

    python命令 -u参数用法解析

    这篇文章主要介绍了python命令 -u参数用法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-10-10
  • python中使用you-get库批量在线下载bilibili视频的教程

    python中使用you-get库批量在线下载bilibili视频的教程

    这篇文章主要介绍了使用python中you-get库批量在线下载bilibili视频的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-03-03
  • 基于PyTorch的permute和reshape/view的区别介绍

    基于PyTorch的permute和reshape/view的区别介绍

    这篇文章主要介绍了基于PyTorch的permute和reshape/view的区别介绍,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-06-06
  • 让python 3支持mysqldb的解决方法

    让python 3支持mysqldb的解决方法

    这篇文章主要介绍了关于让python 3支持mysqldb的解决方法,文中给出解决的示例代码,相信对大家具有一定的参考价值,有需要的朋友可以一起来看看。
    2017-02-02
  • linux环境打包python工程为可执行程序的过程

    linux环境打包python工程为可执行程序的过程

    本次需求,在ubuntu上面开发的python代码程序需要打包成一个可执行程序然后交付给甲方,因为不能直接给源码给甲方,所以寻找方法将python开发的源码打包成一个可执行程序,本次在ubuntu上打包python源码的方法和在window上打包的有点类似,感兴趣的朋友跟随小编一起看看吧
    2024-01-01
  • 浅谈Python type的使用

    浅谈Python type的使用

    今天小编就为大家分享一篇浅谈Python type的使用,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-11-11
  • python 实现多线程下载m3u8格式视频并使用fmmpeg合并

    python 实现多线程下载m3u8格式视频并使用fmmpeg合并

    这篇文章主要介绍了python 实现多线程下载m3u8格式视频,使用fmmpeg合并的实例代码,需要的朋友可以参考下
    2019-11-11
  • Python输出列表(list)的倒序/逆序的几种方法

    Python输出列表(list)的倒序/逆序的几种方法

    列表是一个有序的元素集合,而列表的倒序或逆序操作也是常见的需求之一,本文主要介绍了Python输出列表(list)的倒序/逆序的几种方法,具有一定的参考价值,感兴趣的可以了解一下
    2024-02-02
  • pyodps中的apply用法及groupby取分组排序第一条数据

    pyodps中的apply用法及groupby取分组排序第一条数据

    这篇文章主要介绍了pyodps中的apply用法及groupby取分组排序第一条数据,问绽放围绕主题展开详细的内容介绍,具有一定的参考价值需要的小伙伴可以参考一下
    2022-05-05

最新评论