经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Python » 查看文章
深入理解 Python 虚拟机:复数(complex)的实现原理及源码剖析
来源:cnblogs  作者:一无是处的研究僧  时间:2023/3/14 8:46:56  对本文有异议

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

在本篇文章当中主要给大家介绍在 cpython 虚拟机当中是如何实现 复数 complex 这个数据类型的,这个数据类型在 cpython 当中一应该是一个算比较简单的数据类型了,非常容易理解。

复数数据结构

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

  1. typedef struct {
  2. double real;
  3. double imag;
  4. } Py_complex;
  5. #define PyObject_HEAD PyObject ob_base;
  6. typedef struct {
  7. PyObject_HEAD
  8. Py_complex cval;
  9. } PyComplexObject;
  10. typedef struct _object {
  11. _PyObject_HEAD_EXTRA
  12. Py_ssize_t ob_refcnt;
  13. struct _typeobject *ob_type;
  14. } PyObject;

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

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

  • ob_refcnt,表示对象的引用记数的个数,这个对于垃圾回收很有用处,后面我们分析虚拟机中垃圾回收部分在深入分析。

  • ob_type,表示这个对象的数据类型是什么,在 python 当中有时候需要对数据的数据类型进行判断比如 isinstance, type 这两个关键字就会使用到这个字段。

  • real,表示复数的实部。

  • imag,表示复数的虚部。

复数的操作

复数加法

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

  1. static PyObject *
  2. complex_add(PyObject *v, PyObject *w)
  3. {
  4. Py_complex result;
  5. Py_complex a, b;
  6. TO_COMPLEX(v, a); // TO_COMPLEX 这个宏的作用就是将一个 PyComplexObject 中的 Py_complex 对象存储到 a 当中
  7. TO_COMPLEX(w, b);
  8. result = _Py_c_sum(a, b); // 这个函数的具体实现在下方
  9. return PyComplex_FromCComplex(result); // 这个函数的具体实现在下方
  10. }
  11. // 真正实现复数加法的函数
  12. Py_complex
  13. _Py_c_sum(Py_complex a, Py_complex b)
  14. {
  15. Py_complex r;
  16. r.real = a.real + b.real;
  17. r.imag = a.imag + b.imag;
  18. return r;
  19. }
  20. PyObject *
  21. PyComplex_FromCComplex(Py_complex cval)
  22. {
  23. PyComplexObject *op;
  24. /* Inline PyObject_New */
  25. // 申请内存空间
  26. op = (PyComplexObject *) PyObject_MALLOC(sizeof(PyComplexObject));
  27. if (op == NULL)
  28. return PyErr_NoMemory();
  29. // 将这个对象的引用计数设置成 1
  30. (void)PyObject_INIT(op, &PyComplex_Type);
  31. // 将复数结构体保存下来
  32. op->cval = cval;
  33. return (PyObject *) op;
  34. }

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

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

复数取反

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

  1. static PyObject *
  2. complex_neg(PyComplexObject *v)
  3. {
  4. Py_complex neg;
  5. neg.real = -v->cval.real;
  6. neg.imag = -v->cval.imag;
  7. return PyComplex_FromCComplex(neg);
  8. }
  9. PyObject *
  10. PyComplex_FromCComplex(Py_complex cval)
  11. {
  12. PyComplexObject *op;
  13. /* Inline PyObject_New */
  14. op = (PyComplexObject *) PyObject_MALLOC(sizeof(PyComplexObject));
  15. if (op == NULL)
  16. return PyErr_NoMemory();
  17. (void)PyObject_INIT(op, &PyComplex_Type);
  18. op->cval = cval;
  19. return (PyObject *) op;
  20. }

Repr 函数

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

  1. >>> data = complex(0, 1)
  2. >>> data
  3. 1j
  4. >>> data = complex(1, 1)
  5. >>> data
  6. (1+1j)
  7. >>> print(data)
  8. (1+1j)

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

  1. static PyObject *
  2. complex_repr(PyComplexObject *v)
  3. {
  4. int precision = 0;
  5. char format_code = 'r';
  6. PyObject *result = NULL;
  7. /* If these are non-NULL, they'll need to be freed. */
  8. char *pre = NULL;
  9. char *im = NULL;
  10. /* These do not need to be freed. re is either an alias
  11. for pre or a pointer to a constant. lead and tail
  12. are pointers to constants. */
  13. char *re = NULL;
  14. char *lead = "";
  15. char *tail = "";
  16. // 对应实部等于 0 虚部大于 0 的情况
  17. if (v->cval.real == 0. && copysign(1.0, v->cval.real)==1.0) {
  18. /* Real part is +0: just output the imaginary part and do not
  19. include parens. */
  20. re = "";
  21. im = PyOS_double_to_string(v->cval.imag, format_code,
  22. precision, 0, NULL);
  23. if (!im) {
  24. PyErr_NoMemory();
  25. goto done;
  26. }
  27. } else {
  28. /* Format imaginary part with sign, real part without. Include
  29. parens in the result. */
  30. // 将实部浮点数变成字符串
  31. pre = PyOS_double_to_string(v->cval.real, format_code,
  32. precision, 0, NULL);
  33. if (!pre) {
  34. PyErr_NoMemory();
  35. goto done;
  36. }
  37. re = pre;
  38. // 将虚部浮点数变成字符串
  39. im = PyOS_double_to_string(v->cval.imag, format_code,
  40. precision, Py_DTSF_SIGN, NULL);
  41. if (!im) {
  42. PyErr_NoMemory();
  43. goto done;
  44. }
  45. // 用什么括号包围起来
  46. lead = "(";
  47. tail = ")";
  48. }
  49. result = PyUnicode_FromFormat("%s%s%sj%s", lead, re, im, tail);
  50. done:
  51. PyMem_Free(im);
  52. PyMem_Free(pre);
  53. return result;
  54. }

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

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

总结

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


本篇文章是深入理解 python 虚拟机系列文章之一,文章地址:https://github.com/Chang-LeHung/dive-into-cpython

更多精彩内容合集可访问项目:https://github.com/Chang-LeHung/CSCore

关注公众号:一无是处的研究僧,了解更多计算机(Java、Python、计算机系统基础、算法与数据结构)知识。

原文链接:https://www.cnblogs.com/Chang-LeHung/p/17213485.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号