深入理解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;
上面的数据结构图示如下:
复数的数据在整个 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;
}
我们现在修改源程序将上面的 () 两个括号变成 [],编译之后执行的结果如下所示:
可以看到括号变成了 [] 。
来源:https://www.cnblogs.com/Chang-LeHung/p/17213485.html
0
投稿
猜你喜欢
- 一、super( ) 的用途了解 super() 函数之前,我们首先要知道 super() 的用途是啥?主要用来在子类中调用父类的方法。多用
- Redis 除了做缓存,还能干很多很多事情:分布式锁、限流、处理请求接口幂等性。。。太多太多了配置首先我们创建一个 Spring Boot
- 大部分的pytorch入门教程,都是使用torchvision里面的数据进行训练和测试。如果我们是自己的图片数据,又该怎么做呢?一、我的数据
- 假如文件夹有大量视频文件,需求目标是想从每个视频中提取一帧作为视频的一个封面图片,本文利用opencv-python模块实现需求。结合自己的
- 上一篇:微软建议的ASP性能优化28条守则(6) 技巧 20:避免在循环语句中使用字符串串联 许多人在循环语句中建立一个字符串,如下所示:
- 1.pycharm运行python脚本的过程使用pycharm等编辑器run/debug运行python脚本时,编辑器会通过本地python
- 百度AI接口的调用方法不必多介绍。官网地址人流量统计新建AipBodyAnalysisfrom aip import AipBodyAnal
- 本文实例讲述了python获取指定目录下所有文件名列表的方法。分享给大家供大家参考。具体实现方法如下:这里python代码实现获取文件名列表
- 很简单的一个东西,在'\n'、'\r\n'、'\r'3中换行符之间进行转换。用法usage:
- <!DOCTYPE html><html lang="en"><head><m
- 一、介绍1、SqlLocalDb全称:SQL Server Express LocalDb。简化SQL Server的本地数据库。SqlLo
- python的思维就是让我们用尽可能少的代码来解决问题。对于词频的统计,就代码层面而言,实现的方式也是有很多种的。之所以单独谈到统计词频这个
- 如果是感应触发.就选onmouseover如果是点击触发.就选onclick [把它们两互相替换,就可随时变为感应
- 如下所示:def findSmallest(arr): smallest = arr[0]#将第一个元素的值作为最小值赋给smallest
- 本文介绍了四种asp导出excel数据的方法:1.使用OWC ,2.用Excel的Application组件,3.直接在IE中打开,4.导出
- 代码如下所示:$num = 10.4567; //第一种:利用round()对浮点数进行四舍五入 &n
- cli 的全称 command-line interface(命令行界面),也就是前端同学常用的脚手架,比如 yo、vue cli、reac
- 基于Python2.7的版本环境,Python实现的数据库跨服务器(跨库)迁移, 每以5000条一查询一提交,代码中可以自行更改
- 一、php事务处理概述:事务:是若干事件的集合事务处理:当所有事件执行成功,事务才执行;若有任何一个事件不能成功执行,事务的其它事件也不被执
- 引言要说在工作中最让人头疼的就是用同样的方式处理一堆文件夹中文件,这并不难,但就是繁。所以在遇到机械式的操作时一定要记得使用Python来合