深入理解Python虛擬機中復(fù)數(shù)(complex)的實現(xiàn)原理及源碼剖析
復(fù)數(shù)數(shù)據(jù)結(jié)構(gòu)
在 cpython 當中對于復(fù)數(shù)的數(shù)據(jù)結(jié)構(gòu)實現(xiàn)如下所示:
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;
上面的數(shù)據(jù)結(jié)構(gòu)圖示如下:
復(fù)數(shù)的數(shù)據(jù)在整個 cpython 虛擬機當中來說應(yīng)該算是比較簡單的了,除了一個 PyObject 頭部之外就是實部和虛部了。
- ob_refcnt,表示對象的引用記數(shù)的個數(shù),這個對于垃圾回收很有用處,后面我們分析虛擬機中垃圾回收部分在深入分析。
- ob_type,表示這個對象的數(shù)據(jù)類型是什么,在 python 當中有時候需要對數(shù)據(jù)的數(shù)據(jù)類型進行判斷比如 isinstance, type 這兩個關(guān)鍵字就會使用到這個字段。
- real,表示復(fù)數(shù)的實部。
- imag,表示復(fù)數(shù)的虛部。
復(fù)數(shù)的操作
復(fù)數(shù)加法
下面是 cpython 當中對于復(fù)數(shù)加法的實現(xiàn),為了簡潔刪除了部分無用代碼。
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); // 這個函數(shù)的具體實現(xiàn)在下方 return PyComplex_FromCComplex(result); // 這個函數(shù)的具體實現(xiàn)在下方 } // 真正實現(xiàn)復(fù)數(shù)加法的函數(shù) 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 */ // 申請內(nèi)存空間 op = (PyComplexObject *) PyObject_MALLOC(sizeof(PyComplexObject)); if (op == NULL) return PyErr_NoMemory(); // 將這個對象的引用計數(shù)設(shè)置成 1 (void)PyObject_INIT(op, &PyComplex_Type); // 將復(fù)數(shù)結(jié)構(gòu)體保存下來 op->cval = cval; return (PyObject *) op; }
上面代碼的整體過程比較簡單:
- 首先先從 PyComplexObject 提取真正的復(fù)數(shù)部分。
- 將提取到的兩個復(fù)數(shù)進行相加操作。
- 根據(jù)得到的結(jié)果在創(chuàng)建一個 PyComplexObject 對象,并且將這個對象返回。
復(fù)數(shù)取反
復(fù)數(shù)取反操作就是將實部和虛部取相反數(shù)就可以了,這個操作也比較簡單。
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 函數(shù)
我們現(xiàn)在來介紹一下一個有趣的方法,就是復(fù)數(shù)類型的 repr 函數(shù),這個和類的 __repr__ 函數(shù)是作用是一樣的我們看一下復(fù)數(shù)的輸出是什么:
>>> data = complex(0, 1) >>> data 1j >>> data = complex(1, 1) >>> data (1+1j) >>> print(data) (1+1j)
復(fù)數(shù)的 repr 對應(yīng)的 C 函數(shù)如下所示:
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 = ""; // 對應(yīng)實部等于 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. */ // 將實部浮點數(shù)變成字符串 pre = PyOS_double_to_string(v->cval.real, format_code, precision, 0, NULL); if (!pre) { PyErr_NoMemory(); goto done; } re = pre; // 將虛部浮點數(shù)變成字符串 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; }
我們現(xiàn)在修改源程序?qū)⑸厦娴?() 兩個括號變成 [],編譯之后執(zhí)行的結(jié)果如下所示:
可以看到括號變成了 [] 。
總結(jié)
在本篇文章當中主要給大家介紹了在 cpython 虛擬機當中對于復(fù)數(shù)這一類型的數(shù)據(jù)結(jié)構(gòu)以及他的具體實現(xiàn)??傮w來說這個數(shù)據(jù)結(jié)構(gòu)比較簡單,操作也相對容易,比較容易理解,最后簡單介紹了一下復(fù)數(shù)類型的 repr 實現(xiàn),其實這個函數(shù)和 python 的類型系統(tǒng)有關(guān),目前我們還沒有仔細去討論這一點,在后續(xù)的文章當中我們將深入的去學(xué)習(xí)這個知識點,現(xiàn)在我們就先了解其中部分函數(shù)即可。
到此這篇關(guān)于深入理解Python虛擬機中復(fù)數(shù)(complex)的實現(xiàn)原理及源碼剖析的文章就介紹到這了,更多相關(guān)Python虛擬機 復(fù)數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python中使用you-get庫批量在線下載bilibili視頻的教程
這篇文章主要介紹了使用python中you-get庫批量在線下載bilibili視頻的方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-03-03基于PyTorch的permute和reshape/view的區(qū)別介紹
這篇文章主要介紹了基于PyTorch的permute和reshape/view的區(qū)別介紹,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-06-06linux環(huán)境打包python工程為可執(zhí)行程序的過程
本次需求,在ubuntu上面開發(fā)的python代碼程序需要打包成一個可執(zhí)行程序然后交付給甲方,因為不能直接給源碼給甲方,所以尋找方法將python開發(fā)的源碼打包成一個可執(zhí)行程序,本次在ubuntu上打包python源碼的方法和在window上打包的有點類似,感興趣的朋友跟隨小編一起看看吧2024-01-01python 實現(xiàn)多線程下載m3u8格式視頻并使用fmmpeg合并
這篇文章主要介紹了python 實現(xiàn)多線程下載m3u8格式視頻,使用fmmpeg合并的實例代碼,需要的朋友可以參考下2019-11-11pyodps中的apply用法及groupby取分組排序第一條數(shù)據(jù)
這篇文章主要介紹了pyodps中的apply用法及groupby取分組排序第一條數(shù)據(jù),問綻放圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值需要的小伙伴可以參考一下2022-05-05