网络编程
位置:首页>> 网络编程>> Python编程>> 深入理解Python虚拟机中复数(complex)的实现原理及源码剖析

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

作者:一无是处的研究僧  发布时间:2023-10-24 23:02:50 

标签:Python,虚拟机,复数

复数数据结构

在 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;

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

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

复数的数据在整个 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;
}

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

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

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

来源:https://www.cnblogs.com/Chang-LeHung/p/17213485.html

0
投稿

猜你喜欢

手机版 网络编程 asp之家 www.aspxhome.com