TLDR:Python names 的工作方式类似于具有自动取消/引用的指针,但不允许显式指针操作。其他目标表示间接,其行为类似于指针。
Python 语言规范并没有定义实际的名称等是,只定义了它们的行为方式。不过,这种行为可以用指针来解释。
CPython 实现在底层使用pointers of type PyObject*。因此,可以将名称语义转换为指针操作。关键是将 names 与实际的 objects 分开。
示例 Python 代码包括名称 (i) 和对象 (5)。
i = 5 # name `i` refers to object `5`
j = i # ???
j = 3 # name `j` refers to object `3`
这可以粗略地翻译成具有独立名称和对象的C代码。
int three=3, five=5; // objects
int *i, *j; // names
i = &five; // name `i` refers to position of object `5`
j = i; // name `j` refers to referent of `i`
j = &three; // name `j` refers to position of object `3`
重要的部分是“名称作为指针”不存储对象!我们没有定义*i = five,而是i = &five 。名称和对象相互独立存在。
名称仅指向内存中的现有对象。
当从一个名称分配到另一个名称时,不会交换任何对象!当我们定义j = i 时,这相当于j = &five。 i 和 j 都没有相互连接。
+- name i -+ -\
\
--> + <five> -+
/ | 5 |
+- name j -+ -/ +----------+
因此,changing the target of one name does not affect the other。它只会更新该特定名称所指向的内容。
Python 还有other kinds of name-like elements:属性引用(i.j)、订阅(i[j])和切片(i[:j])。与直接引用对象的名称不同,所有三个都间接引用对象的元素。
示例代码包括名称 (i) 和订阅 (i[0])。
i = [1,2,3] # name `i` refers to object `[1, 2, 3]`
j = i # name `j` refers to referent of `i`
i[0] = 5 # ???
CPython list 在底层使用 PyObject* 指针的 C 数组。这可以再次粗略地转换为具有单独名称和对象的 C 代码。
typedef struct{
int *elements[3];
} list; // length 3 `list` type
int one = 1, two = 2, three = 3, five = 5;
list values = {&one, &two, &three}; // objects
list *i, *j; // names
i = &values; // name `i` refers to object `[1, 2, 3]`
j = i; // name `j` refers to referent of `i`
i->elements[0] = &five; // leading element of `i` refers to object `5`
重要的是我们没有更改任何名称!我们确实更改了i->elements[0],我们的名字都指向的对象的元素。
现有复合对象的值可能会改变。
通过名称更改对象的值时,名称不会更改。 i 和 j 仍然引用同一个对象,我们可以更改其值。
+- name i -+ -\
\
--> + <values> -+
/ | elements | --> [1, 2, 3]
+- name j -+ -/ +-----------+
中间对象的行为类似于指针,我们可以直接更改它所指向的内容并从多个名称中引用它。