> For the complete documentation index, see [llms.txt](https://johannesliu.gitbook.io/data-science-python-implemented/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://johannesliu.gitbook.io/data-science-python-implemented/3.0-basic_data_structure/3.5-dynamics.md).

# 3.5 动态类型

### 3.5.1 变量

在C、C++语言中，如果我们需要使用变量，需要先对变量类型进行声明，而在Python中，我们既不用说明变量的类型，也不用声明变量存在。但是变量在使用前必须被赋值。

对于变量x来说，它在第一次被赋值时创建。也可以说通过赋值，我们创建了变量x。对于变量x来说，变量一旦被创建，并不意味着它的类型就固定了。进一步说，变量永远不会被类型固定，变量永远是通用的。变量通过**引用**特定的对象，可以改变自身数据类型。

### 3.5.2 引用

我们创建的变量都是对对象的引用。要想深入理解这句话的含义，先让我们考虑这样一种情形：为变量x赋值0。

\>>>x=0

Python解释器在完成对变量x赋值的过程中，一共经历了三步：

1. 创建一个值为3的整数对象。
2. 创建一个变量x。如果变量x已经存在，则无需执行此步。
3. 使变量引用在第一步中创建的整数对象。

   变量总是引用对象，而不会出现引用到变量的情况。

   引用是一种关系，这种关系通过内存中的**指针**来实现。当创建变量的时候，解释器就会自动追踪这个变量的引用对象。

   现在我们对x进行多次赋值：

\>>>x=0

\>>>x={1, 2 , 3, 4}

\>>>x=\[1, 2]

\>>>x=”hello world!”

我们首先创建了一个值为0的整数对象，然后创建了一个变量x，使x引用这个整数对象。接着我们使x引用含有元素1， 2， 3， 4的集合对象。最后我们使x引用字符串对象“hello world！”。在这个例子中，我们通过对x进行多次赋值，使x多次引用了不同的对象类型。这也从侧面反映了，变量本身并不对类型进行记忆，实质上，类型在对象中存放。

在Python中，每一个对象都有两个标准的头部信息：**类型标识符**（type desinator）和**引用计数器**（reference counter）。前者标识了对象的类型，后者记录对对象进行回收的时间。

### 3.5.3 垃圾回收

在引用一节中，我们对x进行多次赋值，使其引用不同的对象类型，最终使其指向字符串对象类型。这样的话，我们就有一个疑问：之前我们创建的整型对象，集合对象和列表对象到哪去了？

实质上，这些没有被引用的对象占用的内存被回收了，所以这些对象自然也就消失了。这种自动回收对象空间的技术叫做**垃圾回收**。每当x引用新的对象时，解释器都会对旧对象占用的内存进行回收。这些就空间被放入自由内存空间池，可以被再次利用。

对象中的引用计数器记录了当前引用该对象的变量的数目。一旦这个计数器变为零，就意味着，不再有变量指向这个对象。这时候，解释器就会对这个对象占用的内存空间进行回收。

### 3.5.4 共享引用

当多个变量共同引用一个对象时，就构成了**共享引用**。如果这个对象不支持在原位置修改，那么对变量进行重新赋值就会使变量重新引用新的变量。当所有变量不再引用这个对象时，该对象才会被回收。如果这个对象**支持在原位置进行修改**，对变量进行原位赋值，就会使这个对象发生改变从而影响到所有变量。

例：共享整型引用对象引用

\>>>x=0

\>>>y=x

\>>>x, y

\>>>x=1

\>>>x, y

在上例中，我们先使变量x和y共享为0的整型对象， 然后对x赋值1。此时，x引用了新的值为1的整型变量，y依然引用原有的值为0的整型变量。

我们可以使用身份运算符来确认多变量是否引用到了同一个对象：

例：共享列表引用对象，同时使用身份运算符对判断变量是否引用到同一个对象

\>>>L1=\[1, 2, 3]

\>>>L2=L1

\>>>L1, L2

(\[1, 2, 3], \[1, 2, 3])

\>>>L1=4

\>>>L1, L2, L1 is L2

(4, \[1, 2, 3], False)

\>>>L1=L2

\>>>L2\[0]=4

\>>>L1, L2, L1 is L2

(\[4, 2, 3], \[4, 2, 3], True)

在上例中，我们首先使L1，L2共享引用列表对象\[1, 2, 3]，然后对L1赋值4。此时，L1引用了新的值为4的整型变量，y依然引用原有列表对象\[1, 2, 3]。之后，我们使L1重新引用列表对象，这时L1和L2再次构成了共享引用。之后，我们对变量L2的第0个位置的元素赋值为4。这是，L1和L2依然引用同一个列表对象，不同的是此列表对象的第0个位置的元素值发生了改变，从1变为4。这种改变影响到了所有引用此列表的变量。之所以出现这种情况，是因为列表对象支持在原位置改变，而整数对象则不支持。
