python源码阅读-complex

前言

前面说过,Python3支持intfloatcomplex(复数)3种Number数据类型,本章主要介绍complex

complex

类型对象

complex同样是一个类型对象,其定义在Python/bltinmodule.c文件中:

1
2
3
4
5
...
SETBUILTIN("classmethod", &PyClassMethod_Type);
SETBUILTIN("complex", &PyComplex_Type);
SETBUILTIN("dict", &PyDict_Type);
...

对应着定义在Objects/complexobject.cPyComplex_Type:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
PyTypeObject PyComplex_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"complex",
sizeof(PyComplexObject),
0,
complex_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
(reprfunc)complex_repr, /* tp_repr */
&complex_as_number, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
(hashfunc)complex_hash, /* tp_hash */
0, /* tp_call */
(reprfunc)complex_repr, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
complex_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
complex_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
complex_methods, /* tp_methods */
complex_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
PyType_GenericAlloc, /* tp_alloc */
complex_new, /* tp_new */
PyObject_Del, /* tp_free */
};

complex对象的创建

complex通过complex_new生成complex对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
static PyObject *
complex_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyObject *r, *i, *tmp;
PyNumberMethods *nbr, *nbi = NULL;
Py_complex cr, ci;
int own_r = 0;
int cr_is_complex = 0;
int ci_is_complex = 0;
static char *kwlist[] = {"real", "imag", 0};

r = Py_False;
i = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO:complex", kwlist,
&r, &i))
return NULL;

/* Special-case for a single argument when type(arg) is complex. */
if (PyComplex_CheckExact(r) && i == NULL &&
type == &PyComplex_Type) {
/* Note that we can't know whether it's safe to return
a complex *subclass* instance as-is, hence the restriction
to exact complexes here. If either the input or the
output is a complex subclass, it will be handled below
as a non-orthogonal vector. */
Py_INCREF(r);
return r;
}
if (PyUnicode_Check(r)) {
if (i != NULL) {
PyErr_SetString(PyExc_TypeError,
"complex() can't take second arg"
" if first is a string");
return NULL;
}
return complex_subtype_from_string(type, r);
}
if (i != NULL && PyUnicode_Check(i)) {
PyErr_SetString(PyExc_TypeError,
"complex() second arg can't be a string");
return NULL;
}

tmp = try_complex_special_method(r);
if (tmp) {
r = tmp;
own_r = 1;
}
else if (PyErr_Occurred()) {
return NULL;
}

nbr = r->ob_type->tp_as_number;
if (nbr == NULL || nbr->nb_float == NULL) {
PyErr_Format(PyExc_TypeError,
"complex() first argument must be a string or a number, "
"not '%.200s'",
Py_TYPE(r)->tp_name);
if (own_r) {
Py_DECREF(r);
}
return NULL;
}
if (i != NULL) {
nbi = i->ob_type->tp_as_number;
if (nbi == NULL || nbi->nb_float == NULL) {
PyErr_Format(PyExc_TypeError,
"complex() second argument must be a number, "
"not '%.200s'",
Py_TYPE(i)->tp_name);
if (own_r) {
Py_DECREF(r);
}
return NULL;
}
}

/* If we get this far, then the "real" and "imag" parts should
both be treated as numbers, and the constructor should return a
complex number equal to (real + imag*1j).

Note that we do NOT assume the input to already be in canonical
form; the "real" and "imag" parts might themselves be complex
numbers, which slightly complicates the code below. */
if (PyComplex_Check(r)) {
/* Note that if r is of a complex subtype, we're only
retaining its real & imag parts here, and the return
value is (properly) of the builtin complex type. */
cr = ((PyComplexObject*)r)->cval;
cr_is_complex = 1;
if (own_r) {
Py_DECREF(r);
}
}
else {
/* The "real" part really is entirely real, and contributes
nothing in the imaginary direction.
Just treat it as a double. */
tmp = PyNumber_Float(r);
if (own_r) {
/* r was a newly created complex number, rather
than the original "real" argument. */
Py_DECREF(r);
}
if (tmp == NULL)
return NULL;
if (!PyFloat_Check(tmp)) {
PyErr_SetString(PyExc_TypeError,
"float(r) didn't return a float");
Py_DECREF(tmp);
return NULL;
}
cr.real = PyFloat_AsDouble(tmp);
cr.imag = 0.0;
Py_DECREF(tmp);
}
if (i == NULL) {
ci.real = cr.imag;
}
else if (PyComplex_Check(i)) {
ci = ((PyComplexObject*)i)->cval;
ci_is_complex = 1;
} else {
/* The "imag" part really is entirely imaginary, and
contributes nothing in the real direction.
Just treat it as a double. */
tmp = (*nbi->nb_float)(i);
if (tmp == NULL)
return NULL;
ci.real = PyFloat_AsDouble(tmp);
Py_DECREF(tmp);
}
/* If the input was in canonical form, then the "real" and "imag"
parts are real numbers, so that ci.imag and cr.imag are zero.
We need this correction in case they were not real numbers. */

if (ci_is_complex) {
cr.real -= ci.imag;
}
if (cr_is_complex && i != NULL) {
ci.real += cr.imag;
}
return complex_subtype_from_doubles(type, cr.real, ci.real);
}

说明:

  1. 复数由实数部分和虚数部分构成,可以用 a + bj,或者 complex(a, b) 表示,从上边的代码可以看出,复数的实部a和虚部b都存储为了浮点型;
  2. complex(a, b),当第一个参数为字符串时,不允许出现第二个参数;
  3. complex(a, b),第二个参数不允许是字符串;
  4. complex(a, b),第一个参数必须是字符串或者数字;
  5. complex(a, b),第二个参数必须是数字;

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Python 3.6.4 (v3.6.4:d48ecebad5, Dec 18 2017, 21:07:28)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> complex("1.0", 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: complex() can't take second arg if first is a string
>>> complex(1, "2")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: complex() second arg can't be a string
>>> complex([1], 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: complex() first argument must be a string or a number, not 'list'
>>> complex(1, [2])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: complex() second argument must be a number, not 'list'
>>> complex(1.0, 2.0)
(1+2j)
>>>