--- jupyter: jupytext: text_representation: extension: .md format_name: markdown format_version: '1.2' jupytext_version: 1.3.0rc2 kernelspec: display_name: Python 3 language: python name: python3 --- .. meta:: :description: Topic: variable naming and assignment, Difficulty: Medium, Category: Section :keywords: variable naming, valid names, mutable, immutable, reference, pointer # 变量和赋值
**注**: 本文各处的阅读理解练习旨在帮助你练习使用文章中的知识点。练习题的答案可以在本页结尾找到。
变量的意义在于帮助我们编写灵活易重用的代码。假设我们希望编写代码来记录学生的考试成绩,这过程背后的逻辑不管是记录小明的92分还是小丽的94分都应该是一样的。为此我们可以使用变量,如 `name` 和 `grade`,来作为这些信息的占位符。在本小节中,我们将会展示如何在Python中定义变量。 在Python中,符号 `=` 代表着“赋值”(assignment)操作符。变量在 `=` 左边,而用来赋值的对象在符号的右边: ```python name = "Brian" # 为变量 `name` 赋值字符串 "Brian" grade = 92 # 为变量 `grade` 赋值整数 92 ``` 将左右弄混(如 `92 = name`)会导致语法错误。当变量被某个对象(如一个数字或字符串)赋值时,我们经常会说这变量是该对象的*引用*(reference)。比如说,变量 `name` 是字符串 `"Brian"` 的引用。这意味着,当一个变量被对象赋值后,你可以在代码的其它地方将其作为该对象的代名词(或占位符)使用: ```python # 演示使用变量 name = "Brian" grade = 92 failing = False if grade < 60: failing = True # 在文本文件“student_grades.txt”最后写: # 名字 | 成绩 | 不及格 with open("student_grades.txt", mode="a") as opened_file: opened_file.write("{} | {} | {}".format(name, grade, failing)) ``` ## 变量的合法名字 合法的变量名字可以包括任何字母或数字(`a-z`,`A-Z`,`0-9`)以及下划线(`_`);合法的名字不可以由数字打头。 - `var`:合法 - `_var2`:合法 - `ApplePie_Yum_Yum`:合法 - `2cool`:**不合法**(数字打头) - `I.am.the.best`: **不合法**(包括了 `.`) 变量名也不能和其它Python保留的特殊名字冲突。因此,下列名字也不能作为变量名: - `for`,`while`,`break`,`pass`,`continue` - `in`,`is`,`not` - `if`,`else`,`elif` - `def`,`class`,`return`,`yield`,`raises` - `import`,`from`,`as`,`with` - `try`,`except`,`finally` 还有一些unicode符号可以作为Python变量名中的字符,但本文在此不进行讨论。 ## 可变和不可变对象 对象的**可变性**指它的内态(state)是否能被改变。**可变对象**的内态可以被修改,而**不可变对象**则不能。比如说,列表就是可变对象的一种。在创建之后,我们可以更新列表的内容——代替,增加,或删除它的成员。 ```python # 演示列表的可变性 >>> x = [1, 2, 3] >>> x[0] = -4 # 将 `x` 索引为0的成员修改成 -4 >>> x [-4, 2, 3] ``` 让我们来看看这里具体发生了什么,我们: 1. 创建(初始化)了一个列表,其内态为 `[1, 2, 3]`。 2. 为变量 `x` 赋值了此列表;`x` 现在是该列表的一个引用了。 3. 通过我们的引用变量 `x` 更新了列表索引为0的成员以储存整数 `-4`。 这并没有创建一个新的列表对象,而仅仅*修改*了原本的列表。这也是为什么在命令行中打印 `x` 会显示 `[-4, 2, 3]` 而不是 `[1, 2, 3]`。 元组则是不可变对象的一种。在创建之后,我们没有任何办法可以改变元组的内态;任何看起来像是在更新元组的代码起始都是在创建一个全新的元组。 ```python # 演示元组的不可变性 >>> x = (1, 2, 3) >>> x[0] = -4 # 试图将 `x` 索引为0的成员修改为 -4 --------------------------------------------------------------------------- TypeError Traceback (most recent call last) in () 1 x = (1, 2, 3) ----> 2 x[0] = -4 # attempt to replace element-0 of `x` with -4 TypeError: 'tuple' object does not support item assignment ``` ### 可变和不可变对象类型 以下是Python中常见的一些可变和不可变对象。在我们开始讨论词典(dictionary)和集(set)时这些知识会很有用。 **一些不可变对象** - [数字](https://cn.pythonlikeyoumeanit.com/Module2_EssentialsOfPython/Basic_Objects.html#数字类型)(整数,浮点数,复数) - [字符串](https://cn.pythonlikeyoumeanit.com/Module2_EssentialsOfPython/Basic_Objects.html#字符串) - [元组](https://cn.pythonlikeyoumeanit.com/Module2_EssentialsOfPython/SequenceTypes.html#元组) - [布尔值](https://cn.pythonlikeyoumeanit.com/Module2_EssentialsOfPython/Basic_Objects.html#布尔类) - ["冻结"集](https://cn.pythonlikeyoumeanit.com/Module2_EssentialsOfPython%2FDataStructures_III_Sets_and_More.html#集数据结构)(frozen-set) **一些可变对象** - [列表](https://cn.pythonlikeyoumeanit.com/Module2_EssentialsOfPython/Basic_Objects.html#列表) - [词典](https://cn.pythonlikeyoumeanit.com/Module2_EssentialsOfPython/DataStructures_II_Dictionaries.html) - [集](https://cn.pythonlikeyoumeanit.com/Module2_EssentialsOfPython/DataStructures_III_Sets_and_More.html#集数据结构) - [NumPy数组](https://cn.pythonlikeyoumeanit.com/module_3.html) ## 多个变量引用一个可变对象 你可以向变量赋值给其它已经存在的变量。这将会导致这些变量都引用同一个对象: ```python # 演示引用同一个对象的变量行为 >>> list1 = [1, 2, 3] # `list1` 引用 [1, 2, 3] >>> list2 = list1 # `list2` 和 `list1` 现在都引用 [1, 2, 3] >>> print(list1) [1, 2, 3] >>> print(list2) [1, 2, 3] ``` 具体来讲,这两个变量都引用这列表的*同一个实例*(instance)。这意味着如果列表有变化,所有引用它的变量都会反映这个变化: ```python >>> list1.append(4) # 在 [1, 2, 3] 结尾附加4 >>> print(list1) [1, 2, 3, 4] ``` 我们可以看到 `list2` 还在引用*相同的,更新过的*列表,所以它和 `list1` 同步: ```python >>> print(list2) [1, 2, 3, 4] ``` 一般来讲,为变量 `b` 赋值变量 `a` 会导致它们引用系统内存中*相同*的对象,而为变量 `c` 赋值变量 `a` 或 `b` 会导致有第三个变量引用同样的对象。然后,该对象的任何变化(也就是*mutation*)会在所有引用它的变量中反映(`a`,`b`,和`c`)。 当然,为两个变量赋值内容一样但*不同*的列表意味着一个列表的变化不会影响另外那个: ```python >>> list1 = [1, 2, 3] # `list1` 引用 [1, 2, 3] >>> list2 = [1, 2, 3] # `list2` 引用*另外*一个列表,其值为 [1, 2, 3] >>> list1.append(4) # 在 [1, 2, 3] 结尾附加4 >>> print(list1) [1, 2, 3, 4] >>> print(list2) # `list2` 依然引用它自己的列表 [1, 2, 3] ```
**阅读理解:切片列表是否会创建一个列表的引用?** 设变量 `x`,赋值为一个列表,和变量 `y`,赋值为 `x` 的一个“切片”。`x` 和 `y` 是否引用同样的列表?也就是说,当你更新 `x` 和 `y` 的子序列时,变化是否会在两个变量中都显示?请编写一些简单的代码来查明此问题。
**阅读理解:理解引用** 根据我们对可变和不可变对象的讨论,预测下列情况中 `y` 的最终值: ```python >>> x = 3 >>> y = x # 和 `x = x * 3` 等值 >>> x *= 3 >>> x 9 >>> y ??? ```
## 阅读理解答案 **切片列表是否会创建一个列表的引用?:解** 根据以下的行为,我们可以得出切片列表*并不*创建一个列表的新引用。反而,切片列表会创建对应列表子序列的新列表: ```python >>> x = [0, 1, 2, 3] >>> y = x[:2] >>> y # `y` 是否和 `x` 引用同一个列表? [0, 1] >>> x[0] = -1 # 更新 `x` 引用的列表中的某个成员 >>> x [-1, 1, 2, 3] >>> y # `y` 引用的列表并没有反映这个更新 [0, 1] ``` **理解引用:解** 整数不可变,因此 `x` 肯定是引用一个全新的对象(`9`),而 `y` 还在引用 `3`。