复数数据结构在 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)>>> data1j>>> 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虚拟机中复数的实现原理是什么的详细内容。